目录
-
- 一、为什么要MyBatis配置文件
- 1.1 configuration中元素顺序
- 1.2 properties与environments
-
- 1.3 environment的transactionManager
- 1.4 environment的dataSource
- 1.5 typeAliases、typeHandlers
-
- 1.6 typeAliases
- 1.7 typeHandlers
- 二、plugins、mappers
- 三、settings
- 四、手动创建SqlSessionFactory
-
- 4.1 通过xml文件创建
- 4.2 通过代码创建
- 五、Spring中创建SqlSessionFactory
-
- 5.1 SqlSessionFactoryBean
- 5.2 包扫描
- 六、SpringBoot自动配置
- 七、文档
一、为什么要MyBatis配置文件
其实MyBatis配置文件我们已经很少使用到,因为我们一般不会只是使用MyBatis,而是和Spring一起使用。
在Spring中我们一般会配置一个SqlSessionFactoryBean来创建SqlSessionFactory,而一般不会通过解析配置文件来创建SqlSessionFactory。
但是配置文件很重要,因为它可以帮助我们了解MyBatis中的组件,更好的理解MyBatis的流程和原理。
所以我们首先介绍一下MyBatis的配置文件,然后介绍手动创建SqlSessionFactory,然后介绍Spring和SpringBoot是如何创建SqlSessionFactory。
一般一个数据库对应一个SqlSessionFactory实例
通过SqlSessionFactory获取到SqlSession就能够对数据库进行操作。
1.1 configuration中元素顺序
- properties
- settings
- typeAliases
- typeHandlers
- objectFactory
- objectWrapperFactory
- plugins
- environments
- databaseIdProvider
- mappers
1.2 properties与environments
<?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 resource="dbconfig.properties"><property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/><property name="username" value="${username:tim}"/><property name="password" value="${password:123456}"/></properties><!--default设置为environment的id就可以切换不同的environment--><environments default="development"><environment id="test"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${test.jdbc.driver}" /><property name="url" value="${test.jdbc.url}" /><property name="username" value="${test.jdbc.username}" /><property name="password" value="${test.jdbc.password}" /></dataSource></environment><environment id="development"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="${mybatis.jdbc.driver}" /><property name="url" value="${mybatis.jdbc.url}" /><property name="username" value="${mybatis.jdbc.username}" /><property name="password" value="${mybatis.jdbc.password}" /></dataSource></environment></environments>
</configuration>
properties设置可以访问的属性,3.4之后可以设置默认值
properties的resource可以通过文件导入properties,key=value形式
environments可以包含多个environment,但是只会使用其中一个。
1.3 environment的transactionManager
environment的transactionManager配置事务管理器:
- JDBC(JdbcTransactionFactory)
- MANAGED(ManagedTransactionFactory)
其实JDBC和MANAGED是在Configuration配置类的类型别名注册器中注册的别名
其对应的类分别是JdbcTransactionFactory、ManagedTransactionFactory
在MyBatis的Configuration类中的构造函数中就可以看到:
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
JDBC直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接(Collection)来管理事务作用域
MANAGED配置什么都没做,而是让容器来管理事务的整个生命周期
如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为Spring模块会使用自带的管理器来覆盖前面的配置。
1.4 environment的dataSource
datasource就是数据源,MyBatis有三种内建的数据源类型UNPOOLED、POOLED、JNDI
都是数据源别名,都在MyBatis的Configuration中注册了对应的类:
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
对应JNDI多少几句,JNDI(Java Naming and Directory Interface,Java命名和目录接口),JNDI是Java平台的一个标准扩展,提供一组接口、类和关于命名空间的概念。
简单来说就是,资源提供者想要其它人使用这个资源,就把资源放在一个容器中,并给这个资源一个名称。
其他要使用的人通过名称使用lookup就可以查找到资源,最常见的就算Tomcat配置一个Datasource,在Servlet中就可以使用。
Context context = new InitialContext();
DataSource dataSource = (DataSource) context.lookup("name")
1.5 typeAliases、typeHandlers
<?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="Author" type="org.curitis.bean.Author"/><package name="org.curitis.bean"/></typeAliases><typeHandlers><typeHandler handler="org.curitis.handler.ExampleTypeHandler"/><package name="org.curitis.handler"/></typeHandlers>
</configuration>
typeAliases、typeHandlers都可以指定类或者直接指定要扫描的包,一般2种配置二选一就可以,如果2种都要,package在后。
1.6 typeAliases
typeAliases是为Java类型设置一个短的名字,它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
也可以使用注解方式:
@Alias("user")
public class User {}
1.7 typeHandlers
typeHandlers用于处理JDBC类型到Java类型之间的转换,例如枚举类型转换为数字存储和读取。
2中方式创建自己的typeHandlers:
- 实现:org.apache.ibatis.type.TypeHandler
- 继承:org.apache.ibatis.type.BaseTypeHandler
public class StatusTypeHandler implements TypeHandler<StatusType> {@Overridepublic void setParameter(PreparedStatement ps, int i, StatusType statType, JdbcType jdbcType) throws SQLException {ps.setInt(i, statType.getId());}@Overridepublic StatusType getResult(ResultSet resultSet, String columnName) throws SQLException {return StatusType.getInstance(resultSet.getInt(columnName));}@Overridepublic StatusType getResult(ResultSet resultSet, int columnIndex) throws SQLException {return StatusType.getInstance(resultSet.getInt(columnIndex));}@Overridepublic StatusType getResult(CallableStatement callableStatement, int columnIndex) throws SQLException {return StatusType.getInstance(callableStatement.getInt(columnIndex));}
}
二、plugins、mappers
<?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><plugins><plugin interceptor="com.github.pagehelper.PageHelper"><property name="dialect" value="mysql"/><!-- 默认false,设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 --><property name="offsetAsPageNum" value="true"/><!-- 默认false,设置为true时,使用RowBounds分页会进行count查询 --><property name="rowBoundsWithCount" value="true"/><!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 --><property name="pageSizeZero" value="true"/><!-- 默认false,启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 --><property name="reasonable" value="true"/></plugin></plugins><mappers><mapper resource="org/curitis/mapper/AuthorMapper.xml"/><mapper url="file:///F:/mappers/AuthorMapper.xml"/><mapper class="org.curitis.mapper.AuthorMapper"/><package name="org.curitis.mapper"/></mappers>
</configuration>
plugins就是配置插件,就是实现了MyBatis的Interceptor的类。
如上所示,mapper配置方式多样,处理使用xml配置还可以使用@Mapper注解。
值得注意的是Mapper的xml配置文件的路径最好和Mapper接口的路径一样,否则,如果使用手动创建SqlSessionFactory的时候就可能出现下面的错误。
Mapped Statements collection does not contain value for xxx
一般看到Mapped Statements相关的问题,就可以调试定位问题,断点打到Configuration的getMappedStatement方法中,看一下mappedStatements有没有要执行的方法。
带namespace的和不带namespace的方法都有。
三、settings
<?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="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="autoMappingUnknownColumnBehavior" value="WARNING"/><setting name="defaultExecutorType" value="SIMPLE"/><setting name="defaultStatementTimeout" value="25"/><setting name="defaultFetchSize" value="100"/><setting name="safeRowBoundsEnabled" value="false"/><setting name="mapUnderscoreToCamelCase" value="false"/><setting name="localCacheScope" value="SESSION"/><setting name="jdbcTypeForNull" value="NULL"/><setting name="lazyLoadTriggerMethods" value="equals,clone"/></settings>
</configuration>
<setting name="logImpl" value="STDOUT_LOGGING" />
打印日志,STDOUT_LOGGING是输出到控制台,还可以使用SLF4J、LOG4J、LOG4J2等。
<setting name="returnInstanceForEmptyRow" value="true" />
returnInstanceForEmptyRow默认为false,当列全为空的时候,会返回一个null,这样如果是列表到客户端的时候可能就是[null],很多客户端不能处理,并且没有解决异常就会出问题。
所以可以设置true,返回一个空实例。


四、手动创建SqlSessionFactory
为了更好的理解MyBatis的组件,以及后面在Spring中使用MyBatis,我们先看一下手动创建SqlSessionFactory。

4.1 通过xml文件创建
@Test
public void sessionFactory() throws IOException {String configXml = "mybatis-config.xml";InputStream stream = Resources.getResourceAsStream(configXml);SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(stream);SqlSession sqlSession = sqlSessionFactory.openSession();User user = new User();user.setName("tim");sqlSession.insert("saveUser",user);sqlSession.close();
}
mybatis-config.xml就是Mybatis的配置文件,放在resources目录下就可以了。
xml文件解析是通过MyBatis的XMLConfigBuilder类实现
这里主要是看SqlSessionFactory就不贴User,UserMapper以及对应的xml文件了。
只需要注意saveUser是UserMapper的方法,在xml文件中有对应的id。
4.2 通过代码创建
@Test
public void config(){PooledDataSource dataSource= new PooledDataSource();dataSource.setDriver("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8");dataSource.setUsername("tim");dataSource.setPassword("123456");Environment environment = new Environment("dev", new JdbcTransactionFactory(), dataSource);Configuration config= new Configuration(environment);config.addMappers("org.curitis.mapper");SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(config);SqlSession sqlSession = sqlSessionFactory.openSession(false);User user = new User();user.setName("config");sqlSession.update("org.curitis.mapper.UserMapper.saveUser",user);sqlSession.commit();sqlSession.close();
}
除了通过xml创建,还可以通过代码直接创建,如果理解了上面的代码,对于下面Spring中使用Mybatis的思路就清晰多了。
我们可以看到通过代码创建SqlSessionFactory比xml创建更具灵活性,例如我们可以使用其他的数据库连接池。
五、Spring中创建SqlSessionFactory
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd"><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><list><value>classpath:dbconfig.properties</value></list></property></bean><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"><property name="url" value="${mybatis.jdbc.driver}" /><property name="username" value="${mybatis.jdbc.username}" /><property name="password" value="${mybatis.jdbc.password}" /><property name="initialSize" value="5" /><property name="minIdle" value="5" /><property name="maxActive" value="10" /><property name="maxWait" value="10000" /></bean><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="mapperLocations" value="classpath:org/curitis/mapper/*.xml"/><property name="typeAliasesPackage" value="org.curitis.bean"/><property name="configLocation" value="classpath:mybatis-config.xml"/></bean><mybatis:scan base-package="org.curitis.mapper" factory-ref="sqlSessionFactory" /></beans>
通过Spring使用MyBatis的配置基本就是像上面这样,喜欢注解和自动配置的朋友不要着急,一步一步来,它们都是在这个基础上发展而来的,所以弄清楚上面配置,其他的自动配置当然也不在话下。
其他的不用多说,只需要看SqlSessionFactoryBean这个类和包扫描的部分。
5.1 SqlSessionFactoryBean
先看SqlSessionFactoryBean中MyBatis相关常用属性:
| 属性 | 说明 |
|---|---|
| dataSource | 数据源,应该非常熟悉 |
| configLocation | MyBatis的配置文件资源 |
| mapperLocations | Mapper对应的xml文件位置 |
| typeAliasesPackage | 要配置别名包,会自动添加 |
| typeHandlersPackage | typeHandler包位置 |
看上面的属性是不是和前面介绍MyBatis配置的组件对应上了。
SqlSessionFactoryBean一看就可以猜是一个Spring的FactoryBean,熟悉Spring的同学清楚FactoryBean获取对象是调用getObject方法。
同时SqlSessionFactoryBean还实现了InitializingBean,所以设置完属性之后会调用afterPropertiesSet方法。
在afterPropertiesSet方法中调用了buildSqlSessionFactory,buildSqlSessionFactory就是具体构建SqlSessionFactory的方法。
这里不详细介绍了,有兴趣,断点打进去调试一下就清楚了。
5.2 包扫描
<mybatis:scan base-package="org.curitis" factory-ref="sqlSessionFactory" />
上面是配置包扫描,在Spring中看到xml带有前缀的,找NamespaceHandler就对了,一般就是前缀加上NamespaceHandler这个类,例如MvcNamespaceHandler、DubboNamespaceHandler。
mybatis有点不按常理出牌,他就叫NamespaceHandler:
public class NamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser());}
}
可以看到注册了一个MapperScannerBeanDefinitionParser类来解析mybatis:scan
MapperScannerBeanDefinitionParser创建了了一个MapperScannerConfigurer实例,并添加到Spring中,具体咋创建的key看parseInternal方法。
搞了半天mybatis:scan是创建了一个MapperScannerConfigurer实例,完全可以直接创建一个MapperScannerConfigurer,那用那么多弯弯道道的。
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="org.curitis" />
</bean>
MapperScannerConfigurer发挥器作用的地方在postProcessBeanDefinitionRegistry方法中,有兴趣的朋友可以自己研究。
除了xml配置,我们还可以通过注解:
@MapperScan("org.curitis.mapper")
@MapperScan("org.curitis.*.mapper")
@MapperScan({"org.curitis.user","org.curitis.order"})
六、SpringBoot自动配置
springboot我们使用mybatis-spring-boot-starter,它依赖mybatis-spring-boot-autoconfigure这个包。
自动配置的关键类是MybatisAutoConfiguration,它上面有注解:
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class })
表示如果当前classpath路径下面存在SqlSessionFactory.class和SqlSessionFactoryBean.class这两个类,才满足将当前配置装载到spring容器中的必要条件。
@ConditionalOnSingleCandidate(DataSource.class)
表示当前上下文中有一个DataSource实例的时候才满足将当前配置装载到spring容器中的必要条件
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
表示配置装载在DataSourceAutoConfiguration配置装载之后,这个好理解,因为配置的时候依赖DataSource。
有兴趣可以看一下MybatisAutoConfiguration这个类怎样创建SqlSessionFactory。
七、文档
MyBatis文档