其實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。