MyBatis Notes
Posted on 09/01/2019MyBatis
MyBatis是什么
MyBatis是一个通过xml配置文件或注解将对象(object)和数据库储存程序(stored procedure)与SQL建立关联的Java持久层框架(persistence framework)。
MyBatis是一个在Apache许可证2.0下分发的自由软件。
MyBatis是iBATIS 3.0的分支,且其维护团队包含iBATIS的初创成员。
MyBatis的特点
- 与对象关系映射(ORM)框架不同,MyBatis不将对象和数据库中的表建立联系,而是将Java方法与SQL语句建立关联。
- 相比JDBC,MyBatis简化了开发流程。
- 可以使用数据库的所有功能,比如:储存程序、视图、复杂查询以及制造商专有特性。
- 可以与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语句
-
传统方式:
try { Blog blog = session.selectOne( "org.mybatis.example.BlogMapper.selectBlog", 101); } finally { session.close(); }
-
提供一个接口与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文件可能出现的先后顺序排序):
-
cache,某个命名空间的缓存配置
-
cache-ref,引用其他命名空间的缓存设置
-
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>
-
parameterMap,已经废弃 -
sql,可以被其他元素引用的SQL语句片。
-
insert
-
update
-
delete
-
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);
作用域和生命周期
-
SqlSessionFactoryBuilder: method scope、used and thrown away
-
SqlSessionFactory: application scope, (static) singleton pattern
-
SqlSession: method scope, upon receiving an HTTP request, you can open a SqlSession, then upon returning the response, you can close it.
-
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(); }
不完善的地方
- 对复杂ResultMap的用法的理解还需加强
- MyBatis的实现原理
- mybatis-config.xml:具体的配置细节,typeHandlers
- mapper xml:动态SQL等
参考资料: