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í)候需要自己修改配置,所以稱只為半自動(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í)行查詢 //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ù)的類型。
4、對(duì)結(jié)果集解析麻煩,sql變化導(dǎo)致解析代碼變化,且解析前需要遍歷,如果能將數(shù)據(jù)庫(kù)記錄封裝成pojo對(duì)象解析比較方便。
解決:Mybatis自動(dòng)將sql執(zhí)行結(jié)果映射至java對(duì)象,通過(guò)statement中的resultType定義輸出結(jié)果的類型。
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配置類 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í)行查詢 底層執(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三種類型的數(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)路路徑或者磁盤路徑下的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源碼依賴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)的注釋,還得在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配置類 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ì)查詢出全部的結(jié)果--> <!--如果某些查詢數(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)替代全限定名 //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ì)選擇默認(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ì)用類型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類型。解析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-02Java實(shí)現(xiàn)多用戶注冊(cè)登錄的幸運(yùn)抽獎(jiǎng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)多用戶注冊(cè)登錄的幸運(yùn)抽獎(jiǎng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11