阅读更多
1 MyBatis的基本构成
我们先了解一下MyBatis的核心组件(先不整合Spring,以下内容都是纯MyBatis的概念、使用以及配置方式)
SqlSessionFactoryBuilder
:构造器,它会根据配置信息或者代码来生成SqlSessionFactory
- 利用XML(提取到流对象)或者Java编码(Configuration)对象来构建SqlSessionFactory
- 通过它可以构建多个SessionFactory
- 它的作用就是一个构建器,一旦构建了SqlSessionFactory,它的作用就已经完结,失去了存在的意义
- 它的生命周期只能存在于方法的局部,它的作用就是生成SqlSessionFactory对象
SqlSessionFactory
:会话工厂,用于生产SqlSession
- SqlSessionFactory的作用是创建SqlSession,而SqlSession就是一个会话,相当于JDBC中的Connection对象
- 每次应用程序需要访问数据库,我们就要通过SqlSessionFactory创建SqlSession,所以SqlSessionFactory应该在MyBatis应用的整个生命周期中
- 每个数据库只对应一个SqlSessionFactory
SqlSession
:会话,一个可以发送SQL去执行并返回结果,也可以获取Mapper的接口
- SqlSession是一个会话,相当于JDBC的一个Connection对象,它的生命周期应该是在请求数据库处理事务的过程中
- 它是一个线程不安全的对象
- 每次创建它后,都必须及时关闭它,避免浪费资源
Mapper
:映射器,它由一个Java接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果
- 它存在于一个SqlSession事务方法之内,是一个方法级别的东西
- 它就如同JDBC中一条SQL语句的执行,它最大的范围和SqlSession是相同的
1.1 构建SqlSessionFactory
通过代码构建SqlSessionFactory,需要自己构造Configuration对象,Configuration对象包含了一些基本的配置信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| PooledDataSource dataSource = new PooledDataSource(); dataSource.setDriver("com.mysql.jdbc.Driver"); dataSource.setUsername("root"); dataSource.setPassword("learn");
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment); configuration.getTypeAliasRegistry().registerAlias("role", Role.class); configuration.addMapper(RoleMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
|
通过XML文件构建SqlSessionFactory,在XML文件中进行配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?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> <typeAliases> <typeAlias alias="role" type="com.learn.chapter2.po.Role"/> </typeAliases>
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/>
<dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="learn"/> </dataSource> </environment> </environments>
<mappers> <mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/> </mappers> </configuration>
|
1 2 3
| String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
|
两者本质没有区别,一个Configuration对象就对应了一个MyBatis的XML配置文件
1.2 创建SqlSession
SqlSession是一个接口类,在MyBatis中SqlSession接口的实现类有两个,分别是
- DefaultSqlSession
- SqlSessionManager
SqlSession接口类似于一个JDBC中的Connection接口对象,我们需要保证每次使用完正常关闭它,所以正确的做法是把关闭SqlSession接口的代码写在finally语句中保证每次都会关闭SqlSession,让连接资源归还给数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| SqlSession sqlSession = null; try { sqlSession = sqlSessionFactory.openSession();
sqlSession.commit(); } catch (Exception ex) { System.err.println(ex.getMessage()); } finally { if (sqlSession != null) { sqlSession.close(); } }
|
SqlSession的用途主要有两种
- 获取映射器,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后,返回结果
- 直接通过命名信息去执行SQL,然后返回结果。这是iBatis版本留下的方式。在SqlSession层我们可以通过update、insert、select、delete等方法,带上SQL的id来操作在XML中配置好的SQL
2 MyBatis-XML配置文件
MyBatis配置XML文件的层次结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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> <properties/> <settings/> <typeAliases/> <typeHandlers/> <objectFactory/> <plugins/> <environments> <environment> <transactionManager/> <dataSource/> </environment> </environments> <databaseIdProvider/> <mappers/> </configuration>
|
2.1 别名
别名(typeAliases)是一个指代的名称,用一个简短的名称去指代一个类全限定名,而这个别名可以在MyBatis上下文中使用。在MyBatis中,别名是不区分大小写的
一个typeAliases的实例是在解析配置文件时生成的,然后长期保存在Configuration对象中
2.1.1 系统别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public TypeAliasRegistry() { registerAlias("string", String.class);
registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class);
registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class);
registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class); }
|
2.1.2 自定义别名
我们可以用typeAliases配置别名,也可以用代码方式注册别名
1 2 3 4
| <typeAliases> <typeAlias alias="role" type="com.learn.chapter2.po.Role"/> </typeAliases>
|
如果POJO过多,那么配置起来也是非常麻烦的,MyBatis允许我们通过自动扫描的形式自定义别名
1 2 3
| <typeAliases> <package name="com.learn.chapter2.po"/> </typeAliases>
|
我们可以通过@Alias自定义别名,如下:
1 2 3 4
| @Alias("role") public class Role{ }
|
如果没有@Alias注解,MyBatis也会装载,默认的规则是:将首字母小写的类名,作为MyBatis的别名
2.2 environments配置环境
配置环境可以注册多个数据源(dataSource),每一个数据源分为两大部分:
- 一个是数据源的配置
- 另一个是数据库事务的配置
示例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="autoCommit" value="false"/> </transactionManager>
<dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="learn"/> </dataSource> </environment> </environments>
|
分析一下上面的配置
- environments中的default属性:标明在缺省的情况下,我们将启用哪个数据源配置
- environment元素是配置一个数据源的开始,属性id是这个数据源的标志,以便在MyBatis上下文中使用它
- transactionManager是数据库事务的配置
- 其中type有三种配置方式
- JDBC:采用JDBC方式管理事务,在独立编码中常常使用
- MANAGED:采用容器方式管理事务,在JNDI数据源中常用
- 自定义,由使用者自定义数据库事务管理拌饭,适用于特殊应用
- property元素则是可以配置数据源的各类属性
- dataSource标签,是配置数据源连接的信息
- type属性是提供我们队数据库连接方式的配置,MyBatis支持如下几种配置方式
- UNPOOLED:非连接池数据库,UnpooledDataSrouce
- POOLED:连接池数据库,PooledDataSource
- JNDI:JNDI数据源,JNDIDataSrouce
- property元素可以定义数据库各类参数
2.3 引入映射器的方法
用文件路径引入映射器
1 2 3
| <mappers> <mapper resource="com/learn/chapter3/mapper/roleMapper.xml"/> </mappers>
|
用包名引入映射器
1 2 3
| <mappers> <package name="com.learn.chapter3.mapper"/> </mappers>
|
用类注册引入映射器
1 2 3 4
| <mappers> <mapper class="com.learn.chapter3.mapper.UserMapper"/> <mapper class="com.learn.chapter3.mapper.RoleMapper"/> </mappers>
|
用userMapper.xml引入映射器
1 2 3 4
| <mappers> <mapper url="file:///var/mappers/com/learn/chapter3/mapper/userMapper.xml"/> <mapper url="file:///var/mappers/com/learn/chapter3/mapper/roleMapper.xml"/> </mappers>
|
3 MyBatis-Spring
配置MyBatis-Spring分为下面几个部分
- 配置数据源
- 配置SqlSessionFactory
- 配置SqlSessionTemplate
- 配置Mapper
- 事务处理
在Spring中要构建SqlSessionTemplate对象,让它来产生SqlSession,而在MyBatis-Spring项目中SqlSession的使用是通过SqlSessionTemplate来实现的,它实现了SqlSession接口。所以通过SqlSessionTemplate可以得到Mapper
3.1 配置SqlSessionFactory
MyBatis-Spring项目提供了org.mybatis.spring.SqlSessionFactoryBean
来配置SqlSessionFactory,一般而言需要提供两个参数
- 数据源
- MyBatis配置文件路径
示例:
1 2 3 4 5 6 7 8 9 10 11
| <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="learn"/> </bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:sqlMapConfig.xml"/> </bean>
|
MyBatis配置文件sqlMapConfig.xml
如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?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>
<settings> <setting name="cacheEnabled" value="true"/>
<setting name="useGeneratedKeys" value="true"/>
<setting name="defaultExecutorType" value="REUSE"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="defaultStatementTimeout" value="25000"/> </settings>
<typeAliases> <typeAlias alias="role" type="com.learn.chapter2.po.Role"/> </typeAliases>
<mappers> <mapper resource="com/learn/chapter2/mapper/roleMapper.xml"/> </mappers> </configuration>
|
事实上,SqlSessionFactoryBean已经可以通过Spring IoC配置了,因此,我们完全可以通过Spring IoC来代替原来的配置。SqlSessionFactoryBean包含如下字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| private Resource configLocation;
private Configuration configuration;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
private String environment = SqlSessionFactoryBean.class.getSimpleName();
private boolean failFast;
private Interceptor[] plugins;
private TypeHandler<?>[] typeHandlers;
private String typeHandlersPackage;
private Class<?>[] typeAliases;
private String typeAliasesPackage;
private Class<?> typeAliasesSuperType;
private DatabaseIdProvider databaseIdProvider;
private Class<? extends VFS> vfs;
private Cache cache;
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory;
|
SqlSessionFactoryBean中的这些字段对应的XML配置项如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource"/>
<property name="mapperLocations"/>
<property name="typeAliases"/> <property name="typeAliasesPackage"/> <property name="typeAliasesSuperType"/>
<property name="transactionFactory"/>
<property name="sqlSessionFactoryBuilder"/>
<property name="objectFactory"/> <property name="objectWrapperFactory"/>
<property name="typeHandlers"/> <property name="typeHandlersPackage"/>
<property name="configLocation"/> <property name="configurationProperties"/> <property name="plugins"/> <property name="databaseIdProvider"/> <property name="environment"/> <property name="failFast"/> </bean>
|
大部分情况下,我们无需全部配置,只需要配置其中几项即可
3.2 配置SqlSessionTemplate
3.3 配置Mapper
在大部分场景中,都不建议使用SqlSessionTemplate或者SqlSession,而是推荐采用Mapper接口编程的方式,这样更符合面向对象的编程,也更利于我们理解
3.3.1 MapperFactoryBean
在MyBatis中,Mapper只需要一个接口,而不是一个实现类,它是由MyBatis体系通过动态代理的形式生成代理对象去运行的,所以Spring也没有办法为其生成实现类
为了处理这个问题,MyBatis-Spring团队提供了一个MapperFactoryBean类作为中介,我们可以通过它来实现我们想要的Mapper。配置MapperFactoryBean有三个参数
- mapperInterface:用来定制接口,当我们的接口继承了配置的接口,那么MyBatis就认为它是一个Mapper
- sqlSessionFactory:当sqlSessionTemplate属性不被配置的时候,MyBatis-Spring才会去设置它
- sqlSessionTemplate:当它被设置的时候,sqlSessionFactory将被作废
1 2 3 4 5 6 7
| <bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.learn.dao.UserDAO"/>
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
|
如果每个DAO都需要单独配置,那么工作量会非常大,MyBatis-Spring团队已经考虑到了这种场景,我们可以通过配置MapperScannerConfigurer来实现自动扫描我们的映射器
MapperScannerConfigurer有如下几个属性
- basePackage:指定让Spring自动扫描什么包,它会逐层深入扫描
- annotationClass:表示如果类被这个注解标识的时候,才进行扫描
- sqlSessionFactoryBeanName:指定在Spring中定义sqlSessionFactory的bean名称。如果它被定义,sqlSessionFactory将不起作用
- sqlSessionTemplateBeanName:指定在Spring中定义sqlSessionTemplate的bean的名称。如果它被定义,sqlSessionFactoryBeanName将不起作用
- makerInterface:指定是实现了什么借口就认为它是Mapper,我们需要提供一个公共的接口去标记。在Spring配置前需要给DAO一个注解,在Spring中往往是使用注解@Repository表示DAO层
1 2 3 4 5
| <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.learn.dao"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> <property name="annotationClass" value="org.springframework.stereotype.Repository"/> </bean>
|
这样,Spring上下文就会自动扫描com.learn.dao从而找到标注了Repository的接口,自动生成Mapper
3.4 配置事务
声明式事务配置
1 2 3 4 5 6 7
| <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<tx:annotation-driven transaction-manager="txManager"/>
|
3.5 总结
配置Mapper,是让MyBatis为这些接口生成动态代理,然后根据实际的方法,通过mapperLocations配置的xml找到对应的SQL模板进行调用
4 参考