menu

MyBatis Notes

Posted on 09/01/2019

MyBatis

MyBatis是什么

MyBatis是一个通过xml配置文件或注解将对象(object)和数据库储存程序(stored procedure)与SQL建立关联的Java持久层框架(persistence framework)。

MyBatis是一个在Apache许可证2.0下分发的自由软件。

MyBatis是iBATIS 3.0的分支,且其维护团队包含iBATIS的初创成员。

MyBatis的特点

  1. 与对象关系映射(ORM)框架不同,MyBatis不将对象和数据库中的表建立联系,而是将Java方法与SQL语句建立关联。
  2. 相比JDBC,MyBatis简化了开发流程。
  3. 可以使用数据库的所有功能,比如:储存程序、视图、复杂查询以及制造商专有特性。
  4. 可以与Spring框架或Google Guice整合使用。

理解

  • Mybatis是通过将Java方法与SQL语句建立联系。
  • 通过Mapper接口类定义方法,再在xml配置文件中,mapper元素创建id与接口方法相同的子元素,子元素的内容为相应的sql语句。mapper元素的namespace属性作为定位某一sql的全限定名的前缀。最常用于将xml文件绑定到对应的Mapper接口,此时,namespace 属性值为相应的Mapper接口。
  • SqlSessionFactoryBuilder通过xml配置的mapper文件,创建SqlSessionFactory。调用SqlSessionFactory#openSession() 方法,获得Sqlsession 对象。

MyBatis的使用

创建SqlSessionFactory

每个MyBatis应用程序都围绕着SqlSessionFactory的实例工作,SqlSessionFactory的创建方式如下:

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
  new SqlSessionFactoryBuilder().build(inputStream);

mybatis-config.xml文件包含了数据库的配置信息和映射文件两部分内容,一个简单的配置文件示例如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

其中BlogMapper.xml文件定义了相应的SQL语句。

除了可以通过xml文件创建SqlSessionFactory之外,还可以通过提供一个包含配置信息的Configuration类实例创建:

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory =
  new JdbcTransactionFactory();
Environment environment =
  new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory =
  new SqlSessionFactoryBuilder().build(configuration);

获取SqlSession对象

SqlSession session = sqlSessionFactory.openSession();

执行SQL语句

  1. 传统方式:

    try {
      Blog blog = session.selectOne(
        "org.mybatis.example.BlogMapper.selectBlog", 101);
    } finally {
      session.close();
    }
    
  2. 提供一个接口与Mapper关联的方式:

    try {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      Blog blog = mapper.selectBlog(101);
    } finally {
      session.close();
    }
    

    其中BlogMapper为一个与XXXMapper.xml配置文件相对应的一个接口,该接口的每个方法在Mapper文件中对应一个元素的id属性。

mybatis-config.xml配置文件

MyBatis的Configuration文件包含一些控制其行为的设置和属性,完整的配置元素如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<!--  This file is for a reference purpose for various configuration options -->
	<properties resource="application.properties">
		<property name="username" value="db_user"/>
  	<property name="password" value="verysecurepwd"/>	
	</properties>
	
	<settings>
		  <setting name="cacheEnabled" value="true"/>
		  <setting name="lazyLoadingEnabled" value="true"/>
		  <setting name="multipleResultSetsEnabled" value="true"/>
		  <setting name="useColumnLabel" value="true"/>
		  <setting name="useGeneratedKeys" value="false"/>
		  <setting name="autoMappingBehavior" value="PARTIAL"/>
		  <setting name="defaultExecutorType" value="SIMPLE"/>
		  <setting name="defaultStatementTimeout" value="25000"/>
		  <setting name="safeRowBoundsEnabled" value="false"/>
		  <setting name="mapUnderscoreToCamelCase" value="false"/>
		  <setting name="localCacheScope" value="SESSION"/>
		  <setting name="jdbcTypeForNull" value="OTHER"/>
		  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
	</settings>
	
	 <typeAliases>
	  	<typeAlias alias="Tutor" type="com.mybatis3.domain.Tutor"/>
	  	<package name="com.mybatis3.domain"/>
	 </typeAliases>
	  
   	<typeHandlers>
		  <typeHandler handler="com.mybatis3.typehandlers.PhoneTypeHandler"/>
		  <package name="com.mybatis3.typehandlers"/>
	</typeHandlers>
    	
    <objectFactory></objectFactory>
    
    <pulgins></pulgins>

	 <environments default="development">
	    <environment id="development">
	      <transactionManager type="JDBC"/>
	      <dataSource type="POOLED">
	        <property name="driver" value="${jdbc.driverClassName}"/>
	        <property name="url" value="${jdbc.url}"/>
	        <property name="username" value="${jdbc.username}"/>
	        <property name="password" value="${jdbc.password}"/>
	      </dataSource>
	    </environment>
	    
	    <environment id="production">
	      <transactionManager type="JDBC"/>
	      <dataSource type="JNDI">
	        <property name="data_source" value="java:comp/jdbc/MyBatisDemoDS"/>
	      </dataSource>
	    </environment>    
	  </environments>
	  
     <databaseldProvider></databaseldProvider>
	  <mappers>
	    <mapper resource="com/mybatis3/mappers/StudentMapper.xml"/>
	    <mapper url="file:///var/mappers/StudentMapper.xml"/>
	    <mapper class="com.mybatis3.mappers.TutorMapper"/>
	  </mappers>
  	
</configuration>

Mapper映射语句(Mapped Statements)

MyBatis中Java方法和SQL语句之间的关联是通过映射语句实现的,映射语句可以有两种形式表示,xml文件和Java注解,其中xml文件可以发挥出SQL语句的所有功能,Java注解一般只能使用简单的SQL语句。

xml文件

用于将Java语句和SQL建立关联的文件,以下为一个简单的mapper文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

其中namespace为命名空间,表示的是一个定义了Java方法的Mapper接口,select为mapper文件中对应操作的元素。以下为所有支持的元素(元素在xml文件可能出现的先后顺序排序):

  1. cache,某个命名空间的缓存配置

  2. cache-ref,引用其他命名空间的缓存设置

  3. resultMap,描述如何从数据库结果集中加载对象,是最复杂、功能最强大的元素。

    如果结果集中的列与JavaBean的属性完全对应,则可以通过select语句的返回结果可以通过resultType属性指定,比如:

    <select id="selectUsers" resultType="com.someapp.model.User">
      select id, username, hashedPassword
      from some_table
      where id = #{id}
    </select>
    

    以上,MyBatis会自动创建一个ResultMap,根据结果集的列名和JavaBean的属性进行一一对应。

    如果结果集的列名与JavaBean的属性名不一致,可以通过SQL语句的alias或定义一个ResultMap元素解决。以下为一个简单ResultMap示例:

    <resultMap id="userResultMap" type="User">
      <id property="id" column="user_id" />
      <result property="username" column="user_name"/>
      <result property="password" column="hashed_password"/>
    </resultMap>
    

    如果结果类型为包含有参构造方法,成员变量为自定义类型、集合等复杂情况,则通过ResultMap的子元素constructor,association,collection将结果集对应类型进行关联。以下为一个复杂的ResultMap示例:

    <!-- Very Complex Result Map -->
    <resultMap id="detailedBlogResultMap" type="Blog">
      <constructor>
        <idArg column="blog_id" javaType="int"/>
      </constructor>
      <result property="title" column="blog_title"/>
      <association property="author" javaType="Author">
        <id property="id" column="author_id"/>
        <result property="username" column="author_username"/>
        <result property="password" column="author_password"/>
        <result property="email" column="author_email"/>
        <result property="bio" column="author_bio"/>
        <result property="favouriteSection" column="author_favourite_section"/>
      </association>
      <collection property="posts" ofType="Post">
        <id property="id" column="post_id"/>
        <result property="subject" column="post_subject"/>
        <association property="author" javaType="Author"/>
        <collection property="comments" ofType="Comment">
          <id property="id" column="comment_id"/>
        </collection>
        <collection property="tags" ofType="Tag" >
          <id property="id" column="tag_id"/>
        </collection>
        <discriminator javaType="int" column="draft">
          <case value="1" resultType="DraftPost"/>
        </discriminator>
      </collection>
    </resultMap>
    
  4. parameterMap,已经废弃

  5. sql,可以被其他元素引用的SQL语句片。

  6. insert

  7. update

  8. delete

  9. select,以下为示例:

    <select id="selectPerson" parameterType="int" resultType="hashmap">
      SELECT * FROM PERSON WHERE ID = #{id}
    </select>
    

    表示一个selectPerson的语句,与Mapper接口中的方法想对应,参数类型为int,返回一个以列名为key,对应行的值为value的HashMap。其中#{id}告诉MyBatis作类似如下操作:

    // Similar JDBC code, NOT MyBatis…
    String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
    PreparedStatement ps = conn.prepareStatement(selectPerson);
    ps.setInt(1,id);
    

Java注解

Java方法不是通过xml文件与SQL语句建立关联,而是通过注解,但该方法只能使用简单的SQL语句,复杂的SQL语句则需要使用xml文件。

public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

MyBatis与Spring框架的整合使用

将SqlSession的创建与管理交由spring的IoC容器,以下为整合的配置:

<!-- database config -->
<util:properties id="dbConfig" location="classpath:db.properties"</util:properties>

<!-- datasource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="#{dbConfig.driver}"></property>
	<property name="url" value="#{dbConfig.url}"></property>
	<property name="username" value="#{dbConfig.username}"></property>
	<property name="password" value="#{dbConfig.password}"></property>
</bean>

<!-- SqlsessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"></property>
	<property name="mapperLocations" value="classpath:mappers/*Mapper.xml"></property>
</bean>

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
	 <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

<!-- spring 和 mybatis整合 -->
<bean id="scannerConfig" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="com.philowong.dao"></property>
</bean>

以下为调用方式:

AbstractApplicationContext ac = 
    new ClassPathXmlApplicationContext("application_dao.xml");
AddressDao dao = ac.getBean("addressDao",AddressDao.class);

作用域和生命周期

  1. SqlSessionFactoryBuilder: method scope、used and thrown away

  2. SqlSessionFactory: application scope, (static) singleton pattern

  3. SqlSession: method scope, upon receiving an HTTP request, you can open a SqlSession, then upon returning the response, you can close it.

  4. Mapper Instance: Instances of the mapper interfaces are acquired from the SqlSession, same as SqlSession.

    SqlSession session = sqlSessionFactory.openSession();
    try {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      // do work
    } finally {
      session.close();
    }
    

不完善的地方

  1. 对复杂ResultMap的用法的理解还需加强
  2. MyBatis的实现原理
  3. mybatis-config.xml:具体的配置细节,typeHandlers
  4. mapper xml:动态SQL等

参考资料:

  1. MyBaits 3.0官网
  2. Java Persistence with MyBatis 3
Top