MyBatis配置文件解析與MyBatis實(shí)例演示
MyBatis介紹
MyBatis是一個(gè)持久層的ORM框架,使用簡(jiǎn)單,學(xué)習(xí)成本較低??梢詧?zhí)行自己手寫(xiě)的SQL語(yǔ)句,比較靈活。但是MyBatis的自動(dòng)化程度不高,移植性也不高,有時(shí)從一個(gè)數(shù)據(jù)庫(kù)遷移到另外一個(gè)數(shù)據(jù)庫(kù)的時(shí)候需要自己修改配置,所以稱(chēng)只為半自動(dòng)ORM框架
傳統(tǒng)JDBC和Mybatis相比的弊病
傳統(tǒng)JDBC
@Test
public void test() throws SQLException {
Connection conn=null;
PreparedStatement pstmt=null;
try {
// 1.加載驅(qū)動(dòng)
Class.forName("com.mysql.jdbc.Driver");
// 2.創(chuàng)建連接
conn= DriverManager.
getConnection("jdbc:mysql://localhost:3306/mybatis_example", "root", "123456");
// SQL語(yǔ)句
String sql="select id,user_name,create_time from t_user where id=?";
// 獲得sql執(zhí)行者
pstmt=conn.prepareStatement(sql);
pstmt.setInt(1,1);
// 執(zhí)行查詢(xún)
//ResultSet rs= pstmt.executeQuery();
pstmt.execute();
ResultSet rs= pstmt.getResultSet();
rs.next();
User user =new User();
user.setId(rs.getLong("id"));
user.setUserName(rs.getString("user_name"));
user.setCreateTime(rs.getDate("create_time"));
System.out.println(user.toString());
} catch (Exception e) {
e.printStackTrace();
}
finally{
// 關(guān)閉資源
try {
if(conn!=null){
conn.close();
}
if(pstmt!=null){
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
傳統(tǒng)JDBC的問(wèn)題如下:
1.數(shù)據(jù)庫(kù)連接創(chuàng)建,釋放頻繁造成西戎資源的浪費(fèi),從而影響系統(tǒng)性能,使用數(shù)據(jù)庫(kù)連接池可以解決問(wèn)題。
2.sql語(yǔ)句在代碼中硬編碼,造成代碼的不已維護(hù),實(shí)際應(yīng)用中sql的變化可能較大,sql代碼和java代碼沒(méi)有分離開(kāi)來(lái)維護(hù)不方便。
3.使用preparedStatement向有占位符傳遞參數(shù)存在硬編碼問(wèn)題因?yàn)閟ql中的where子句的條件不確定,同樣是修改不方便/
4.對(duì)結(jié)果集中解析存在硬編碼問(wèn)題,sql的變化導(dǎo)致解析代碼的變化,系統(tǒng)維護(hù)不方便。
mybatis對(duì)傳統(tǒng)的JDBC的解決方案
1、數(shù)據(jù)庫(kù)連接創(chuàng)建、釋放頻繁造成系統(tǒng)資源浪費(fèi)從而影響系統(tǒng)性能,如果使用數(shù)據(jù)庫(kù)連接池可解決此問(wèn)題。
解決:在SqlMapConfig.xml中配置數(shù)據(jù)連接池,使用連接池管理數(shù)據(jù)庫(kù)鏈接。
2、Sql語(yǔ)句寫(xiě)在代碼中造成代碼不易維護(hù),實(shí)際應(yīng)用sql變化的可能較大,sql變動(dòng)需要改變java代碼。
解決:將Sql語(yǔ)句配置在XXXXmapper.xml文件中與java代碼分離。
3、向sql語(yǔ)句傳參數(shù)麻煩,因?yàn)閟ql語(yǔ)句的where條件不一定,可能多也可能少,占位符需要和參數(shù)一一對(duì)應(yīng)。
解決:Mybatis自動(dòng)將java對(duì)象映射至sql語(yǔ)句,通過(guò)statement中的parameterType定義輸入?yún)?shù)的類(lèi)型。
4、對(duì)結(jié)果集解析麻煩,sql變化導(dǎo)致解析代碼變化,且解析前需要遍歷,如果能將數(shù)據(jù)庫(kù)記錄封裝成pojo對(duì)象解析比較方便。
解決:Mybatis自動(dòng)將sql執(zhí)行結(jié)果映射至java對(duì)象,通過(guò)statement中的resultType定義輸出結(jié)果的類(lèi)型。
Mybaits整體體系圖


一個(gè)Mybatis最簡(jiǎn)單的使用例子如下:
public class App {
public static void main(String[] args) {
String resource = "mybatis-config.xml";
Reader reader;
try {
//將XML配置文件構(gòu)建為Configuration配置類(lèi)
reader = Resources.getResourceAsReader(resource);
// 通過(guò)加載配置文件流構(gòu)建一個(gè)SqlSessionFactory DefaultSqlSessionFactory
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
// 數(shù)據(jù)源 執(zhí)行器 DefaultSqlSession
SqlSession session = sqlMapper.openSession();
try {
// 執(zhí)行查詢(xún) 底層執(zhí)行jdbc
//User user = (User)session.selectOne("com.tuling.mapper.selectById", 1);
UserMapper mapper = session.getMapper(UserMapper.class);
System.out.println(mapper.getClass());
User user = mapper.selectById(1L);
System.out.println(user.getUserName());
} catch (Exception e) {
e.printStackTrace();
}finally {
session.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
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>
<!--properties 掃描屬性文件.properties -->
<properties resource="db.properties"></properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<plugins>
<plugin interceptor="com.tuling.plugins.ExamplePlugin" ></plugin>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--// mybatis內(nèi)置了JNDI、POOLED、UNPOOLED三種類(lèi)型的數(shù)據(jù)源,其中POOLED對(duì)應(yīng)的實(shí)現(xiàn)為org.apache.ibatis.datasource.pooled.PooledDataSource,它是mybatis自帶實(shí)現(xiàn)的一個(gè)同步、線程安全的數(shù)據(jù)庫(kù)連接池 一般在生產(chǎn)中,我們會(huì)使用c3p0或者druid連接池-->
<dataSource type="POOLED">
<property name="driver" value="${mysql.driverClass}"/>
<property name="url" value="${mysql.jdbcUrl}"/>
<property name="username" value="${mysql.user}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--1.必須保證接口名(例如IUserDao)和xml名(IUserDao.xml)相同,還必須在同一個(gè)包中-->
<package name="com.tuling.mapper"/>
<!--2.不用保證同接口同包同名
<mapper resource="com/mybatis/mappers/EmployeeMapper.xml"/>
3.保證接口名(例如IUserDao)和xml名(IUserDao.xml)相同,還必須在同一個(gè)包中
<mapper class="com.mybatis.dao.EmployeeMapper"/>
4.不推薦:引用網(wǎng)路路徑或者磁盤(pán)路徑下的sql映射文件 file:///var/mappers/AuthorMapper.xml
<mapper url="file:E:/Study/myeclipse/_03_Test/src/cn/sdut/pojo/PersonMapper.xml"/>-->
</mappers>
</configuration>
總結(jié)下就是分為下面四個(gè)步驟:
- 從配置文件(通常是XML文件)得到SessionFactory;
- 從SessionFactory得到SqlSession;
- 通過(guò)SqlSession進(jìn)行CRUD和事務(wù)的操作;
- 執(zhí)行完相關(guān)操作之后關(guān)閉Session。
MyBatis 源碼編譯
1.下載mybatis源碼
下載地址:https://github.com/mybatis/mybatis-3

我下載的最新的 mybatis-3-mybatis-3.4.6,下載完后解壓。打開(kāi)pom.xml
<parent>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-parent</artifactId>
<version>30</version>
<relativePath />
</parent>發(fā)現(xiàn)mybatis源碼依賴(lài)mybatis-parent 所以編譯前要先下載mybatis-parent
2.下載mybatis-parent源碼
下載地址:https://github.com/mybatis/parent

下載的mybatis-parent版本要和mybatis源文件pom.xml 版本一致。
3.編譯mybatis-parent源碼
切換到你下載的mybatis-parent目錄:
mvn clean install
3.編譯mybatis源碼
切換到你下載的mybatis源碼目錄:
mvn clean mvn install -Dmaven.test.skip=true
如果出現(xiàn)如下錯(cuò)誤:

打開(kāi)pom.xml 文件注釋掉 maven-pdf-plugin 插件
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pdf-plugin</artifactId>
</plugin>
-->然后重新編譯
4.導(dǎo)入IDEA
導(dǎo)入方式就不多說(shuō),按maven項(xiàng)目導(dǎo)入即可!導(dǎo)入成功后:

如果希望在spring源碼中引入你自己的這份源碼,可以做如下操作
1.修改你mybatis源碼的pom的 這樣可以和官方的區(qū)分開(kāi)來(lái)
<version>3.5.3-xsls</version>
2.這樣你在spring源碼中就可以引入這份mybatis源碼了.
- 如果引入mybatis-spring 同樣需要做1、3步驟
compile("org.mybatis:mybatis-spring:2.0.3-xsls")
compile("org.mybatis:mybatis:3.5.3-xsls")
3.當(dāng)然,如果你想在spring這邊看到你mybatis源碼相關(guān)的注釋?zhuān)€得在mybatis源碼的pom里面加入plugin,使它生成 jar 的同時(shí) 生成 sources 包
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<attach>true</attach>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
Mybatis啟動(dòng)流程分析
String resource = "mybatis-config.xml"; //將XML配置文件構(gòu)建為Configuration配置類(lèi) reader = Resources.getResourceAsReader(resource); // 通過(guò)加載配置文件流構(gòu)建一個(gè)SqlSessionFactory DefaultSqlSessionFactory SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
通過(guò)上面代碼發(fā)現(xiàn),創(chuàng)建SqlSessionFactory的代碼在SqlSessionFactoryBuilder中,進(jìn)去一探究竟:
//整個(gè)過(guò)程就是將配置文件解析成Configration對(duì)象,然后創(chuàng)建SqlSessionFactory的過(guò)程
//Configuration是SqlSessionFactory的一個(gè)內(nèi)部屬性
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
下面我們看下解析配置文件過(guò)程中的一些細(xì)節(jié)。
先給出一個(gè)配置文件的例子:
<?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>
<!--SqlSessionFactoryBuilder中配置的配置文件的優(yōu)先級(jí)最高;config.properties配置文件的優(yōu)先級(jí)次之;properties標(biāo)簽中的配置優(yōu)先級(jí)最低 -->
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</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="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="STATEMENT"/>-->
<!--<setting name="jdbcTypeForNull" value="OTHER"/>-->
<!--<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>-->
<!--<setting name="logImpl" value="STDOUT_LOGGING" />-->
</settings>
<typeAliases>
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--默認(rèn)值為 false,當(dāng)該參數(shù)設(shè)置為 true 時(shí),如果 pageSize=0 或者 RowBounds.limit = 0 就會(huì)查詢(xún)出全部的結(jié)果-->
<!--如果某些查詢(xún)數(shù)據(jù)量非常大,不應(yīng)該允許查出所有數(shù)據(jù)-->
<property name="pageSizeZero" value="true"/>
</plugin>
</plugins>
<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://10.59.97.10:3308/windty"/>
<property name="username" value="windty_opr"/>
<property name="password" value="windty!234"/>
</dataSource>
</environment>
</environments>
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql" />
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<mappers>
<!--這邊可以使用package和resource兩種方式加載mapper-->
<!--<package name="包名"/>-->
<!--<mapper resource="./mappers/SysUserMapper.xml"/>-->
<mapper resource="./mappers/CbondissuerMapper.xml"/>
</mappers>
</configuration>
下面是解析配置文件的核心方法:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析properties標(biāo)簽,并set到Configration對(duì)象中
//在properties配置屬性后,在Mybatis的配置文件中就可以使用${key}的形式使用了。
propertiesElement(root.evalNode("properties"));
//解析setting標(biāo)簽的配置
Properties settings = settingsAsProperties(root.evalNode("settings"));
//添加vfs的自定義實(shí)現(xiàn),這個(gè)功能不怎么用
loadCustomVfs(settings);
//配置類(lèi)的別名,配置后就可以用別名來(lái)替代全限定名
//mybatis默認(rèn)設(shè)置了很多別名,參考附錄部分
typeAliasesElement(root.evalNode("typeAliases"));
//解析攔截器和攔截器的屬性,set到Configration的interceptorChain中
//MyBatis 允許你在已映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下,MyBatis 允許使用插件來(lái)攔截的方法調(diào)用包括:
//Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
//ParameterHandler (getParameterObject, setParameters)
//ResultSetHandler (handleResultSets, handleOutputParameters)
//StatementHandler (prepare, parameterize, batch, update, query)
pluginElement(root.evalNode("plugins"));
//Mybatis創(chuàng)建對(duì)象是會(huì)使用objectFactory來(lái)創(chuàng)建對(duì)象,一般情況下不會(huì)自己配置這個(gè)objectFactory,使用系統(tǒng)默認(rèn)的objectFactory就好了
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//設(shè)置在setting標(biāo)簽中配置的配置
settingsElement(settings);
//解析環(huán)境信息,包括事物管理器和數(shù)據(jù)源,SqlSessionFactoryBuilder在解析時(shí)需要指定環(huán)境id,如果不指定的話(huà),會(huì)選擇默認(rèn)的環(huán)境;
//最后將這些信息set到Configration的Environment屬性里面
environmentsElement(root.evalNode("environments"));
//
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//無(wú)論是 MyBatis 在預(yù)處理語(yǔ)句(PreparedStatement)中設(shè)置一個(gè)參數(shù)時(shí),還是從結(jié)果集中取出一個(gè)值時(shí), 都會(huì)用類(lèi)型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類(lèi)型。解析typeHandler。
typeHandlerElement(root.evalNode("typeHandlers"));
//解析Mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

上面解析流程結(jié)束后會(huì)生成一個(gè)Configration對(duì)象,包含所有配置信息,然后會(huì)創(chuàng)建一個(gè)SqlSessionFactory對(duì)象,這個(gè)對(duì)象包含了Configration對(duì)象。
簡(jiǎn)單總結(jié)
對(duì)于MyBatis啟動(dòng)的流程(獲取SqlSession的過(guò)程)這邊簡(jiǎn)單總結(jié)下:
- SqlSessionFactoryBuilder解析配置文件,包括屬性配置、別名配置、攔截器配置、環(huán)境(數(shù)據(jù)源和事務(wù)管理器)、Mapper配置等;解析完這些配置后會(huì)生成一個(gè)Configration對(duì)象,這個(gè)對(duì)象中包含了MyBatis需要的所有配置,然后會(huì)用這個(gè)Configration對(duì)象創(chuàng)建一個(gè)SqlSessionFactory對(duì)象,這個(gè)對(duì)象中包含了Configration對(duì)象;
這里解析的東西比較多,大致概況:會(huì)把所有的信息都解析到Configration對(duì)象中,比較簡(jiǎn)單不多介紹。
更多關(guān)于MyBatis配置文件解析與MyBatis實(shí)例演示以及怎樣編譯安裝MyBatis請(qǐng)查看下面的相關(guān)鏈接
相關(guān)文章
SpringBoot集成WebSocket實(shí)現(xiàn)后臺(tái)向前端推送信息
在一次項(xiàng)目開(kāi)發(fā)中,使用到了Netty網(wǎng)絡(luò)應(yīng)用框架,以及MQTT進(jìn)行消息數(shù)據(jù)的收發(fā),這其中需要后臺(tái)來(lái)將獲取到的消息主動(dòng)推送給前端,所以本文記錄了SpringBoot集成WebSocket實(shí)現(xiàn)后臺(tái)向前端推送信息的操作,需要的朋友可以參考下2024-02-02
Java實(shí)現(xiàn)多用戶(hù)注冊(cè)登錄的幸運(yùn)抽獎(jiǎng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)多用戶(hù)注冊(cè)登錄的幸運(yùn)抽獎(jiǎng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11

