1.什么是MyBatis?
MyBatis 是一款優(yōu)秀的持久層框架脸狸,它支持自定義 SQL最仑、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數(shù)和獲取結(jié)果集的工作炊甲。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型泥彤、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數(shù)據(jù)庫中的記錄卿啡。
2.MyBatis中的主要組成部分
-
SqlSessionFactoryBuilder( 工廠構(gòu)造器 ) :根據(jù)配置文件來生成SqlSessionFactory
這個類可以被實例化吟吝、使用和丟棄,一旦創(chuàng)建了 SqlSessionFactory颈娜,就不再需要它了剑逃。 因此SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用SqlSessionFactoryBuilder 來創(chuàng)建多個 SqlSessionFactory 實例揭鳞,但最好還是不要一直保留著它炕贵,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
SqlSessionFactoryBuilder創(chuàng)建代碼示例:SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
-
SqlSessionFactory(SqlSession工廠):創(chuàng)建SqlSession野崇。
SqlSessionFactory 一旦被創(chuàng)建就應該在應用的運行期間一直存在称开,沒有任何理由丟棄它或重新創(chuàng)建另一個實例。 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重復創(chuàng)建多次乓梨,多次重建 SqlSessionFactory被視為一種代碼“壞習慣”鳖轰。因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到扶镀,最簡單的就是使用單例模式或者靜態(tài)單例模式蕴侣。
SqlSessionFactory創(chuàng)建代碼示例:InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
-
SqlSession(會話):執(zhí)行SQL并返回結(jié)果。
每個線程都應該有它自己的 SqlSession 實例臭觉。SqlSession 的實例不是線程安全的昆雀,因此是不能被共享的辱志,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 SqlSession 實例的引用放在一個類的靜態(tài)域狞膘,甚至一個類的實例變量也不行揩懒。 也絕不能將 SqlSession 實例的引用放在任何類型的托管作用域中,比如 Servlet 框架中的 HttpSession挽封。 如果你現(xiàn)在正在使用一種 Web 框架已球,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說辅愿,每次收到 HTTP 請求智亮,就可以打開一個 SqlSession,返回一個響應后点待,就關(guān)閉它阔蛉。這個關(guān)閉操作很重要,為了確保每次都能執(zhí)行關(guān)閉操作亦鳞,你應該把這個關(guān)閉操作放到 finally 塊中馍忽。 下面的示例就是一個確保 SqlSession 關(guān)閉的標準模式:
SqlSession創(chuàng)建代碼示例:try (SqlSession session = sqlSessionFactory.openSession()) { // 應用邏輯代碼 }
-
SQL Mapper:由一個Java接口和XML文件(或注解)構(gòu)成棒坏。接口負責定義數(shù)據(jù)訪問接口燕差,
XML文件(或注解)負責定義SQL和映射規(guī)則。
映射器是一些綁定映射語句的接口坝冕。映射器接口的實例是從 SqlSession 中獲得的徒探。雖然從技術(shù)層面上來講,任何映射器實例的最大作用域與請求它們的 SqlSession 相同喂窟。但方法作用域才是映射器實例的最合適的作用域测暗。 也就是說,映射器實例應該在調(diào)用它們的方法中被獲取磨澡,使用完畢之后即可丟棄碗啄。 映射器實例并不需要被顯式地關(guān)閉。盡管在整個請求作用域保留映射器實例不會有什么問題稳摄,但是你很快會發(fā)現(xiàn)稚字,在這個作用域上管理太多像 SqlSession 的資源會讓你忙不過來。 因此厦酬,最好將映射器放在方法作用域內(nèi)胆描。就像下面的例子一樣:
代碼示例:try (SqlSession session = sqlSessionFactory.openSession()) { BlogMapper mapper = session.getMapper(BlogMapper.class); // 你的應用邏輯代碼 }
3.使用單例設計模式(懶漢式)設計一個用于生成SqlSession的MyBatis工具類
代碼實現(xiàn):
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtils {
//靜態(tài)字符串保存主配置文件的路徑
private static final String CONFIG = "mybatis-config.xml";
//聲明靜態(tài)SqlSessionFactory確保一個程序中只會有一個工廠類
private static SqlSessionFactory factory;
//創(chuàng)建同步鎖確保線程安全
private static final Class<MyBatisUtils02> CLASS_LOCK = MyBatisUtils02.class;
//靜態(tài)代碼塊創(chuàng)建工廠類
static {
factory = initSqlSessionFactory();
}
private MyBatisUtils02() {
}
/**
* 返回創(chuàng)建的工廠
* @return SqlSessionFactory對象
*/
private static SqlSessionFactory initSqlSessionFactory() {
if(factory == null) {
//加同步鎖確保只會創(chuàng)建一個工廠。
synchronized (CLASS_LOCK) {
try (InputStream in = Resources.getResourceAsStream(CONFIG)){
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return factory;
}
/**
* @return 一個個SqlSession對象
*/
public static SqlSession openSqlSession() {
if(factory == null) {
initSqlSessionFactory();
}
return factory.openSession();
}
}
4.MyBatis配置
- 引入外部的properties文件
主要作用:加載外部的properties文件
引入文件(properties)代碼示例:<properties resource="jdbc.properties"/>
properties文件中的內(nèi)容:
driver_class = com.mysql.cj.jdbc.Driver
jdbc_url = jdbc:mysql://服務器IP地址/數(shù)據(jù)庫名稱?useSSL=false&serverTimezone=UCT
db_username = 用戶賬號
db_password = 用戶密碼
- 設置(settings)
主要作用:MyBatis框架運行規(guī)則配置
設置(settings)代碼示例:<settings> <!-- 開啟日志 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
- 類型別名(typeAliases)
主要作用:設置別名(為實體類設置別名
類型別名(typeAliases)代碼示例:<typeAliases> <!-- 使用 typeAlias 將實體類進行單一的重命名--> <typeAlias type="com.apesource.entity.Employee"alias="EmployeeMapper" /> <!-- 設置實體類包仗阅,為該package中的每個實體類自動設置別名 --> <package name="com.apesource.entity"/> </typeAliases>
- 環(huán)境配置(environments)
主要作用:配置當前的環(huán)境(開發(fā)環(huán)境昌讲,測試環(huán)境等等),數(shù)據(jù)庫連接信息减噪。
環(huán)境配置(environments)代碼示例:<environments default="development"> <!-- 環(huán)境1:開發(fā)環(huán)境 --> <environment id="development"> <!-- 使用JDBC的事物管理機制 --> <transactionManager type="JDBC" /> <!-- 啟用數(shù)據(jù)庫連接池 --> <dataSource type="POOLED"> <!-- 如果加載了外部的 Properties 文件則可以直接使用這樣方式賦值數(shù)據(jù)庫參數(shù) --> <!-- 注意value大括號中的內(nèi)容和 Properties 文件中的鍵對應--> <property name="driver" value="${driver_class}" /> <property name="url" value="${jdbc_url}" /> <property name="username" value="${db_username}" /> <property name="password" value="${db_password}" /> </dataSource> </environment> </environments>
- 映射器(mappers)
主要作用:傳遞映射器的地址短绸。
映射器(mappers)代碼示例:
<mappers> <!-- 映射XML文件 --> <mapper resource="com/apesource/dao/mapper/EmployeeMapper.xml" /> <!-- 映射接口 --> <mapper class="com.apesource.dao.mapper.OrderMapper" /> </mappers>
5.MyBatis XML 映射器
1. 常用節(jié)點作用總結(jié)
- select :用SQL語句查詢數(shù)據(jù)庫中的數(shù)據(jù)车吹。
- update:用SQL語句修改數(shù)據(jù)庫中的數(shù)據(jù)。
- delete:用SQL語句刪除數(shù)據(jù)庫中的數(shù)據(jù)醋闭。
- insert:用SQL語句向數(shù)據(jù)庫添加數(shù)據(jù)礼搁。
2.常用屬性作用總結(jié)
- namespace屬性:mapper節(jié)點中對應映射器接口的完全限定名。
- id 屬性:接口中的方法名目尖。
- resultType 屬性:該方法的返回類型馒吴。
- parameterType 屬性:該方法的返回參數(shù)類型。
- useGeneratedKeys屬性:是否開啟主鍵回填瑟曲。
- keyProperty屬性:主鍵回填后保存的位置饮戳。
3.常見SQL映射示例
示例1:普通增加
<insert id="insertAnswerRecord" parameterType="AnswerRecord" useGeneratedKeys="true" keyProperty="recordId"> INSERT INTO answer_record(respondent,question,right_answer,submit_answer,submit_datetime) VALUES(#{respondent},#{question},#{rightAnswer},#{submitAnswer},#{submitDatetime}) </insert>
接口方法定義:int insertAnswerRecord(AnswerRecord answerRecord);
示例2:批量增加
<!-- foreach 用于動態(tài)SQL的循環(huán)遍歷,Collection 指定遍歷的集合類型洞拨,item為每次循環(huán)遍歷的元素命名 --> <insert id="insertAnswerRecordBatch" parameterType="list"> INSERT INTO answer_record(respondent,question,right_answer,submit_answer,submit_datetime) VALUES <foreach collection="list" item="record" separator=","> ( #{record.respondent}, #{record.question}, #{record.rightAnswer}, #{record.submitAnswer}, now() ) </foreach> </insert>
接口方法定義:int insertAnswerRecordBatch(List<AnswerRecord> answerRecordsList);
示例3:普通刪除
<delete id="deleteAnswerRecord"> DELETE FROM answer_record WHERE record_id LIKE #{recordId} </delete>
接口方法定義:int deleteAnswerRecord(int recordId);
示例4:批量刪除
<!-- 批量刪除答題記錄 --> <update id="deleteAnswerRecordBatch" parameterType="list"> DELETE FROM answer_record WHERE record_id IN <foreach collection="list" item="rid" open="(" close=")" separator=","> #{rid} </foreach> </update>
接口方法定義:int deleteAnswerRecordBatch(List<Integer> count);
示例5:動態(tài)修改
<update id="updateAnswerRecord" parameterType="AnswerRecord"> UPDATE answer_record <set> <if test="respondent!=null">respondent = #{respondent},</if> <if test="question!=null">question = #{question},</if> <if test="rightAnswer!=null">right_answer = #{rightAnswer},</if> <if test="submitAnswer!=null">submit_answer = #{submitAnswer},</if> submit_datetime = now() </set> WHERE record_id = #{recordId} </update>
接口方法定義:int updateAnswerRecord(AnswerRecord answerRecord);
示例6:動態(tài)查詢
<select id="listAnswerRecordByCondition" resultType="AnswerRecord" parameterType="AnswerRecord"> SELECT record_id as recordId, respondent as respondent, question as question, right_answer as rightAnswer, submit_answer as submitAnswer, submit_datetime as submitDatetime FROM answer_record <where> <if test="respondent!=null">AND respondent = #{respondent}</if> <if test="question!=null">AND question LIKE concat('%',#{question},'%')</if> <if test="rightAnswer!=null">AND right_answer = #{rightAnswer}</if> <if test="submitAnswer!=null">AND submit_answer = #{submitAnswer}</if> </where> </select>
接口方法定義:List<AnswerRecord> listAnswerRecordByCondition(AnswerRecord answerRecord);
示例7:查詢結(jié)果封裝為Map
<select id="countAnswerRecordDataByRespondent" resultType="map"> SELECT COUNT(record_id) AS sum, (SELECT COUNT(record_id) FROM answer_record WHERE right_answer = submit_answer AND respondent = #{respondent}) AS correct, (SELECT COUNT(record_id) FROM answer_record WHERE right_answer != submit_answer AND respondent = #{respondent}) AS total FROM answer_record WHERE respondent = #{respondent} </select>
接口方法定義:Map<String,Integer> countAnswerRecordDataByRespondent(String respondent);
6.MyBatis的緩存機制
- Mybatis的一級緩存是SqlSession
- MyBatis的二級緩存是SqlSessionFactory
如何設置二級緩存
主配置文件配置
<!-- MyBatis框架運行規(guī)則配置 -->
<settings>
<!-- 開啟二級緩存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
mapper映射文件配置
<!--
eviction:代表的是緩存回收策略扯罐,目前MyBatis提供以下策略。
(1)LRU烦衣,最近最少使用的歹河,回收最長時間不用的對象
(2)FIFO,先進先出花吟,按對象進入緩存的順序來移除他們
(3)SOFT秸歧,軟引用,移除基于垃圾回收器狀態(tài)和軟引用規(guī)則對象
(4)WEAK衅澈,弱引用键菱,更積極的移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
這里采用的是LRU今布,移除最長時間不用的對象
flushInterval:刷新間隔時間经备,單位為毫秒,這里配置的是100秒刷新部默;
如果沒有配置侵蒙,當SQL被執(zhí)行的時候才會去刷新緩存。
size:引用數(shù)目傅蹂,一個正整數(shù)纷闺,代表緩存最多可以存儲多少個對象,不宜設置過大贬派。
設置過大會導致內(nèi)存溢出急但。這里配置的是1024個對象
readOnly:只讀,意味著緩存數(shù)據(jù)只能讀取而不能修改搞乏,這樣設置的好處是我們可以快速讀取緩存波桩,
缺點是我們沒辦法修改緩存,他的默認值是false请敦,不允許我們修改镐躲。
-->
<!-- cache節(jié)點:配置當前命名空間下的mapper的緩存配置 -->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024" />