其实MyBatis配置文件咱们已经不多使用到,由于咱们通常不会只是使用MyBatis,而是和Spring一块儿使用。html
在Spring中咱们通常会配置一个SqlSessionFactoryBean来建立SqlSessionFactory,而通常不会经过解析配置文件来建立SqlSessionFactory。java
可是配置文件很重要,由于它能够帮助咱们了解MyBatis中的组件,更好的理解MyBatis的流程和原理。mysql
因此咱们首先介绍一下MyBatis的配置文件,而后介绍手动建立SqlSessionFactory,而后介绍Spring和SpringBoot是如何建立SqlSessionFactory。git
通常一个数据库对应一个SqlSessionFactory实例github
经过SqlSessionFactory获取到SqlSession就可以对数据库进行操做。spring
<?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形式sql
environments能够包含多个environment,可是只会使用其中一个。数据库
environment的transactionManager配置事务管理器:apache
其实JDBC和MANAGED是在Configuration配置类的类型别名注册器中注册的别名 其对应的类分别是JdbcTransactionFactory、ManagedTransactionFactoryspringboot
在MyBatis的Configuration类中的构造函数中就能够看到:
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
JDBC直接使用了JDBC的提交和回滚设置,它依赖于从数据源获得的链接(Collection)来管理事务做用域
MANAGED配置什么都没作,而是让容器来管理事务的整个生命周期
若是你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 由于Spring模块会使用自带的管理器来覆盖前面的配置。
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")
<?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在后。
typeAliases是为Java类型设置一个短的名字,它只和XML配置有关,存在的意义仅在于用来减小类彻底限定名的冗余。
也可使用注解方式:
@Alias("user") public class User { }
typeHandlers用于处理JDBC类型到Java类型之间的转换,例如枚举类型转换为数字存储和读取。
2中方式建立本身的typeHandlers:
public class StatusTypeHandler implements TypeHandler<StatusType> { @Override public void setParameter(PreparedStatement ps, int i, StatusType statType, JdbcType jdbcType) throws SQLException { ps.setInt(i, statType.getId()); } @Override public StatusType getResult(ResultSet resultSet, String columnName) throws SQLException { return StatusType.getInstance(resultSet.getInt(columnName)); } @Override public StatusType getResult(ResultSet resultSet, int columnIndex) throws SQLException { return StatusType.getInstance(resultSet.getInt(columnIndex)); } @Override public StatusType getResult(CallableStatement callableStatement, int columnIndex) throws SQLException { return StatusType.getInstance(callableStatement.getInt(columnIndex)); } }
<?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的方法都有。
<?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,返回一个空实例。
为了更好的理解MyBatis的组件,以及后面在Spring中使用MyBatis,咱们先看一下手动建立SqlSessionFactory。
@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。
@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建立更具灵活性,例如咱们可使用其余的数据库链接池。
<?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.xsd http://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这个类和包扫描的部分。
先看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的方法。
这里不详细介绍了,有兴趣,断点打进去调试一下就清楚了。
<mybatis:scan base-package="org.curitis" factory-ref="sqlSessionFactory" />
上面是配置包扫描,在Spring中看到xml带有前缀的,找NamespaceHandler就对了,通常就是前缀加上NamespaceHandler这个类,例如MvcNamespaceHandler、DubboNamespaceHandler。
mybatis有点不按常理出牌,他就叫NamespaceHandler:
public class NamespaceHandler extends NamespaceHandlerSupport { @Override public 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咱们使用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。