什么是 MyBatis?
MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數(shù)據(jù)庫中的記錄。
使用步驟
1. Maven依賴
<dependency>
<groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
使用mysql的driver。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
2. XML中構(gòu)建 SqlSessionFactory
XML 配置文件中包含了對 MyBatis 系統(tǒng)的核心設(shè)置,包括獲取數(shù)據(jù)庫連接實(shí)例的數(shù)據(jù)源(DataSource)以及決定事務(wù)作用域和控制方式的事務(wù)管理器(TransactionManager)。
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
3. 編寫工具類
這就可以根據(jù)config.xml文件中數(shù)據(jù)庫的連接配置來創(chuàng)建SqlSessionFactory,并編寫提供sqlSession的方法。
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
4. 編寫實(shí)體類
根據(jù)數(shù)據(jù)庫中的數(shù)據(jù)編寫。
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
5. 編寫DAO層
public interface UserDao {
List<User> getUserList();
}
原本的JDBC中需要編寫具體的實(shí)現(xiàn)類,從而執(zhí)行SQL語句。但是Mybatis通過使用XML定義的方式取代了編寫實(shí)現(xiàn)類,并取名為Mapper,其中namespace實(shí)現(xiàn)接口綁定,select中的id屬性就是接口中的方法,resultType表示了返回值的類型,就是我們編寫的實(shí)體類。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zzy.dao.UserDao">
<select id="getUserList" resultType="com.zzy.pojo.User">
select * from mybatis.test
</select>
</mapper>
此時還需要在Mybatis的config.xml中配置,指定mapper的路徑。
6. 編寫測試類
通過工具類MybatisUtils獲取SqlSession,getmapper執(zhí)行mapper中的配置,相當(dāng)于構(gòu)建接口的實(shí)現(xiàn)類,最后調(diào)用了getUserList,執(zhí)行g(shù)etUserList方法,返回查詢的結(jié)果,最后一定記得將資源關(guān)閉。
@Test
public void test() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for(User user : userList){
System.out.println(user);
}
sqlSession.close();
}
生命周期和作用域
SqlSessionFactoryBuilder
這個類可以被實(shí)例化、使用和丟棄,一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實(shí)例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用 SqlSessionFactoryBuilder 來創(chuàng)建多個 SqlSessionFactory 實(shí)例,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在,沒有任何理由丟棄它或重新創(chuàng)建另一個實(shí)例。 使用 SqlSessionFactory 的最佳實(shí)踐是在應(yīng)用運(yùn)行期間不要重復(fù)創(chuàng)建多次,多次重建 SqlSessionFactory 被視為一種代碼“壞習(xí)慣”。因此 SqlSessionFactory 的最佳作用域是應(yīng)用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態(tài)單例模式。
SqlSession
每個線程都應(yīng)該有它自己的 SqlSession 實(shí)例。SqlSession 的實(shí)例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 SqlSession 實(shí)例的引用放在一個類的靜態(tài)域,甚至一個類的實(shí)例變量也不行。 也絕不能將 SqlSession 實(shí)例的引用放在任何類型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現(xiàn)在正在使用一種 Web 框架,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說,每次收到 HTTP 請求,就可以打開一個 SqlSession,返回一個響應(yīng)后,就關(guān)閉它。 這個關(guān)閉操作很重要,為了確保每次都能執(zhí)行關(guān)閉操作,你應(yīng)該把這個關(guān)閉操作放到 finally 塊中。 下面的示例就是一個確保 SqlSession 關(guān)閉的標(biāo)準(zhǔn)模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的應(yīng)用邏輯代碼
}
如果不及時關(guān)閉,會造成大量的資源浪費(fèi)。
映射器實(shí)例
映射器是一些綁定映射語句的接口。映射器接口的實(shí)例是從 SqlSession 中獲得的。雖然從技術(shù)層面上來講,任何映射器實(shí)例的最大作用域與請求它們的 SqlSession 相同。但方法作用域才是映射器實(shí)例的最合適的作用域。 也就是說,映射器實(shí)例應(yīng)該在調(diào)用它們的方法中被獲取,使用完畢之后即可丟棄。 映射器實(shí)例并不需要被顯式地關(guān)閉。盡管在整個請求作用域保留映射器實(shí)例不會有什么問題,但是你很快會發(fā)現(xiàn),在這個作用域上管理太多像 SqlSession 的資源會讓你忙不過來。 因此,最好將映射器放在方法作用域內(nèi)。就像下面的例子一樣:
try (SqlSession session = sqlSessionFactory.openSession()) { BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的應(yīng)用邏輯代碼
}
XML配置
enviroments
mybatis可以配置多種環(huán)境,這種機(jī)制有助于SQL映射到多種數(shù)據(jù)庫中, 現(xiàn)實(shí)情況下有多種理由需要這么做。例如,開發(fā)、測試和生產(chǎn)環(huán)境需要有不同的配置;或者想在具有相同 Schema 的多個生產(chǎn)數(shù)據(jù)庫中使用相同的 SQL 映射。還有許多類似的使用場景。
但是每個sqlsessionfactory只能對應(yīng)一個環(huán)境,而每個數(shù)據(jù)庫對應(yīng)一個sqlsessionfactory。
為了指定創(chuàng)建哪種環(huán)境,只要將它作為可選的參數(shù)傳遞給 SqlSessionFactoryBuilder 即可??梢越邮墉h(huán)境配置的兩個方法簽名是:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
如果沒有寫明指定的環(huán)境,就會使用默認(rèn)環(huán)境,default=環(huán)境id,就是默認(rèn)的環(huán)境。
事務(wù)管理器(transactionManager)
在 MyBatis 中有兩種類型的事務(wù)管理器(也就是 type="[JDBC|MANAGED]")
● JDBC – 這個配置直接使用了 JDBC 的提交和回滾設(shè)施,它依賴從數(shù)據(jù)源獲得的連接來管理事務(wù)作用域。
● MANAGED – 這個配置幾乎沒做什么。它從不提交或回滾一個連接,而是讓容器來管理事務(wù)的整個生命周期(比如 JEE 應(yīng)用服務(wù)器的上下文)。 默認(rèn)情況下它會關(guān)閉連接。然而一些容器并不希望連接被關(guān)閉,因此需要將 closeConnection 屬性設(shè)置為 false 來阻止默認(rèn)的關(guān)閉行為。例如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
數(shù)據(jù)源(dataSource)
dataSource 元素使用標(biāo)準(zhǔn)的 JDBC 數(shù)據(jù)源接口來配置 JDBC 連接對象的資源。
有三種內(nèi)建的數(shù)據(jù)源類型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
- UNPOOLED–這個數(shù)據(jù)源的實(shí)現(xiàn)會每次請求時打開和關(guān)閉連接。雖然有點(diǎn)慢,但對那些數(shù)據(jù)庫連接可用性要求不高的簡單應(yīng)用程序來說,是一個很好的選擇。
- POOLED– 這種數(shù)據(jù)源的實(shí)現(xiàn)利用“池”的概念將 JDBC 連接對象組織起來,避免了創(chuàng)建新的連接實(shí)例時所必需的初始化和認(rèn)證時間。這種處理方式很流行,能使并發(fā) Web 應(yīng)用快速響應(yīng)請求。
- JNDI – 這個數(shù)據(jù)源實(shí)現(xiàn)是為了能在如 EJB或應(yīng)用服務(wù)器這類容器中使用,容器可以集中或在外部配置數(shù)據(jù)源,然后放置一個 JNDI 上下文的數(shù)據(jù)源引用。
屬性(properties)
property可以通過編寫.properties文件來配置,或者直接在environment中聲明,如果使用.properties就需要在properties的resource中指明路徑,然后在環(huán)境中使用properties時,使用如下形式,${}里面對應(yīng)的就是.properties文件中屬性名。
如果同時.properties和在xml文件配置,那么就會優(yōu)先使用.properties。
類型別名(typeAliases)
類型別名可為 Java 類型設(shè)置一個縮寫名字。 它僅用于 XML 配置,意在降低冗余的全限定類名書寫。兩種聲明方式:
給每一個類起一個具體的別名,這種方式的好處是可以自己指定。
掃描包下的類,自動給類起別名,別名默認(rèn)為這個類的首字母小寫的名字。比如com.blog.User的別名為user,可以在類上添加Alias注解也可以實(shí)現(xiàn)自定義別名。
@Alias("author")
public class Author {
...
}
設(shè)置(settings)
這是 MyBatis 中極為重要的調(diào)整設(shè)置,它們會改變 MyBatis 的運(yùn)行時行為。 下表描述了設(shè)置中各項(xiàng)設(shè)置的含義、默認(rèn)值等。
主要掌握:
log4j
Log4j是Apache的一個開源項(xiàng)目,通過使用Log4j,我們可以控制日志信息輸送的目的地是控制臺、文件、GUI組件,甚至是套接口服務(wù)器、NT的事件記錄器、UNIX Syslog守護(hù)進(jìn)程等;我們也可以控制每一條日志的輸出格式;通過定義每一條日志信息的級別,我們能夠更加細(xì)致地控制日志的生成過程。最令人感興趣的就是,這些可以通過一個配置文件來靈活地進(jìn)行配置,而不需要修改應(yīng)用的代碼。
使用步驟:
第一步:在maven中導(dǎo)入jar包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
第二步:編寫log4j.properties文件
log4j.rootLogger=DEBUG,console,file
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%c]-%m%n
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/zzy.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
log4j.appender.org.mybatis=DEBUG
log4j.appender.java.sql=DEBUG
log4j.appender.java.sql.Statement=DEBUG
log4j.appender.java.sql.ResultSet=DEBUG
log4j.appender.java.sql.PreparedStatement=DEBUG
第三步:配置
注意要完全一樣,大小寫、空格等不同都會導(dǎo)致錯誤。
第四步:獲取當(dāng)前使用類的對象。
static Logger logger = Logger.getLogger(xxx.class);
效果:
映射器(mappers)
既然 MyBatis 的行為已經(jīng)由上述元素配置完了,我們現(xiàn)在就要來定義 SQL 映射語句了。 但首先,我們需要告訴 MyBatis 到哪里去找到這些語句。
XML映射文件
參數(shù)映射
鑒于參數(shù)類型(parameterType)會被自動設(shè)置為int,這個參數(shù)可以隨意命名。
如果 User 類型的參數(shù)對象傳遞到了語句中,會查找 id、username 和 password 屬性,然后將它們的值傳入預(yù)處理語句的參數(shù)中。
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
提示 :JDBC 要求,如果一個列允許使用 null 值,并且會使用值為 null 的參數(shù),就必須要指定 JDBC 類型(jdbcType)。
盡管上面這些選項(xiàng)很強(qiáng)大,但大多時候,你只須簡單指定屬性名,頂多要為可能為空的列指定jdbcType,其他的事情交給 MyBatis 自己去推斷就行了。
#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}
使用@Param 可以指定mapper接收的參數(shù)名。
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column,
@Param("value") String value);
結(jié)果映射
MyBatis 的真正強(qiáng)大在于它的語句映射,這是它的魔力所在。由于它的異常強(qiáng)大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進(jìn)行對比,你會立即發(fā)現(xiàn)省掉了將近 95% 的代碼。MyBatis 致力于減少使用成本,讓用戶能更專注于SQL代碼。
resultMap
resultmap元素是 MyBatis 中最重要最強(qiáng)大的元素。它可以讓你從 90% 的 JDBC resultset數(shù)據(jù)提取代碼中解放出來,并在一些情形下允許你進(jìn)行一些 JDBC 不支持的操作。實(shí)際上,在為一些比如連接的復(fù)雜語句編寫映射代碼的時候,一份 resultmap 能夠代替實(shí)現(xiàn)同等功能的數(shù)千行代碼。ResultMap 的設(shè)計(jì)思想是,對簡單的語句做到零配置,對于復(fù)雜一點(diǎn)的語句,只需要描述語句之間的關(guān)系就行了。
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password from some_table where id = #{id}
</select>
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
ResultMap的優(yōu)秀之處——你完全可以不用顯式地配置它們,當(dāng)實(shí)體類的屬性名和數(shù)據(jù)庫的列名一樣時,就完全不需要使用(自動映射),只需要在不相同的時候配置即可。
動態(tài)SQL
if
使用動態(tài) SQL 最常見情景是根據(jù)條件包含 where 子句的一部分。比如:
<select id="findActiveBlogWithTitleLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
如果希望通過 “title” 和 “author” 兩個參數(shù)進(jìn)行可選搜索該怎么辦呢?首先,我想先將語句名稱修改成更名副其實(shí)的名稱;接下來,只需要加入另一個條件即可。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
choose、when、otherwise
有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點(diǎn)像 Java 中的 switch 語句。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
trim、where、set
現(xiàn)在看下面這個列子,當(dāng)沒有一個匹配時,sql就為:select * from blog where;這種顯然時錯誤的語句,那么如果第二個語句成立時,語句變?yōu)椋簊elect * from blog where and title like #{title};顯然也是錯誤的語句。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
MyBatis 有一個簡單且適合大多數(shù)場景的解決辦法。而在其他場景中,可以對其進(jìn)行自定義以符合需求。而這,只需要一處簡單的改動:
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
where 元素只會在子元素返回任何內(nèi)容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們?nèi)コ?/p>
如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定制 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素為:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides 屬性會忽略通過管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內(nèi)容,并且插入 prefix 屬性中指定的內(nèi)容。
用于動態(tài)更新語句的類似解決方案叫做 set。set 元素可以用于動態(tài)包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},
</if>
<if test="password != null">password=#{password},
</if>
<if test="email != null">email=#{email},
</if>
<if test="bio != null">bio=#{bio}
</if>
</set>
where id=#{id}
</update>
這個例子中,set 元素會動態(tài)地在行首插入 SET 關(guān)鍵字,并會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。
來看看與 set 元素等價的自定義 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
foreach
動態(tài) SQL 的另一個常見使用場景是對集合進(jìn)行遍歷(尤其是在構(gòu)建 IN 條件語句的時候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</select>
foreach 元素的功能非常強(qiáng)大,它允許你指定一個集合,聲明可以在元素體內(nèi)使用的集合項(xiàng)(item)和索引(index)變量。它也允許你指定開頭與結(jié)尾的字符串以及集合項(xiàng)迭代之間的分隔符。這個元素也不會錯誤地添加多余的分隔符,看它多智能!
提示:你可以將任何可迭代對象(如 List、Set 等)、Map 對象或者數(shù)組對象作為集合參數(shù)傳遞給 foreach。當(dāng)使用可迭代對象或者數(shù)組時,index 是當(dāng)前迭代的序號,item 的值是本次迭代獲取到的元素。當(dāng)使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。
bind
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG WHERE title LIKE #{pattern}
</select>
緩存
基本概念
MyBatis 內(nèi)置了一個強(qiáng)大的事務(wù)性查詢緩存機(jī)制,它可以非常方便地配置和定制。 為了使它更加強(qiáng)大而且易于配置,我們對 MyBatis 3 中的緩存實(shí)現(xiàn)進(jìn)行了許多改進(jìn)。
默認(rèn)情況下,只啟用了本地的會話緩存,它僅僅對一個會話中的數(shù)據(jù)進(jìn)行緩存。 要啟用全局的二級緩存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
同時還需要在settings中聲明cacheEnable,雖然是默認(rèn)為true的。
基本上就是這樣。這個簡單語句的效果如下:
- 映射語句文件中的所有 select 語句的結(jié)果將會被緩存。
- 映射語句文件中的所有 insert、update 和 delete語句會刷新緩存。
- 緩存會使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。
- 緩存不會定時進(jìn)行刷新(也就是說,沒有刷新間隔)。
- 緩存會保存列表或?qū)ο螅o論查詢方法返回哪種)的 1024 個引用。
- 緩存會被視為讀/寫緩存,這意味著獲取到的對象并不是共享的,可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改。
提示:緩存只作用于 cache 標(biāo)簽所在的映射文件中的語句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的語句將不會被默認(rèn)緩存。你需要使用 @CacheNamespaceRef 注解指定緩存作用域。
這些屬性可以通過 cache 元素的屬性來修改。
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
這個更高級的配置創(chuàng)建了一個 FIFO 緩存,每隔 60 秒刷新,最多可以存儲結(jié)果對象或列表的 512 個引用,而且返回的對象被認(rèn)為是只讀的,因此對它們進(jìn)行修改可能會在不同線程中的調(diào)用者產(chǎn)生沖突。
可用的清除策略有:
默認(rèn)的清除策略是 LRU。
flushInterval(刷新間隔)屬性可以被設(shè)置為任意的正整數(shù),設(shè)置的值應(yīng)該是一個以毫秒為單位的合理時間量。 默認(rèn)情況是不設(shè)置,也就是沒有刷新間隔,緩存僅僅會在調(diào)用語句時刷新。
size(引用數(shù)目)屬性可以被設(shè)置為任意正整數(shù),要注意欲緩存對象的大小和運(yùn)行環(huán)境中可用的內(nèi)存資源。默認(rèn)值是 1024。
readOnly(只讀)屬性可以被設(shè)置為 true 或 false。只讀的緩存會給所有調(diào)用者返回緩存對象的相同實(shí)例。 因此這些對象不能被修改。這就提供了可觀的性能提升。而可讀寫的緩存會(通過序列化)返回緩存對象的拷貝。 速度上會慢一些,但是更安全,因此默認(rèn)值是 false。
工作流程
二級緩存臟讀
由于二級緩存作用于namesace級別,也就是說不同mapper有自己獨(dú)立一套的緩存,那么當(dāng)不同mapper對同一表進(jìn)行操作時,就會出現(xiàn)臟讀。
比如mapper A 查詢了 M 表 ,然后mapper B 對 M表修改并提交事務(wù),只要mapper A這段時間內(nèi)沒有執(zhí)行update、delete、insert操作,mapper A的緩存就不會被刷新,當(dāng)mapper A再次select時,由于Mybatis緩存工作機(jī)制,會先從二級緩存中拿取數(shù)據(jù),此時mapper A select出的數(shù)據(jù)就是臟數(shù)據(jù)。
因此在開啟二級緩存后,所有的對同一表的增刪改查應(yīng)該在同一mapper下。
Mybatis執(zhí)行流程
-
resource讀取配置文件
-
解析配置文件流XML
-
Configuration
-
實(shí)列化SqlSessionFactory
-
transactionManager
-
executor
-
創(chuàng)建SqlSession
-
實(shí)現(xiàn)CRUD
結(jié)語
感謝你看到這里,文章有什么不足還請指正,覺得文章對你有幫助的話記得給我點(diǎn)個贊,每天都會分享java相關(guān)技術(shù)文章或行業(yè)資訊,歡迎大家關(guān)注和轉(zhuǎn)發(fā)文章!
歡迎關(guān)注公眾號:前程有光,領(lǐng)取一線大廠Java面試題總結(jié)+各知識點(diǎn)學(xué)習(xí)思維導(dǎo)+一份300頁pdf文檔的Java核心知識點(diǎn)總結(jié)!