【精選必看】MyBatis映射文件及動態SQL,一級,二級緩存介紹
文章目錄
MyBatis映射文件

< r e s u l t M a p > <resultMap> <resultMap>
MyBatis映射文件中除了<insert>、<delete>、<update>、<select>外,還有一些標簽可以使用:
resultMap
標簽的作用的自定義映射關系。
MyBatis可以將數據庫結果集封裝到對象中,是因為結果集的列名和對象屬性名相同:
當POJO屬性名和數據庫列名不一致時,MyBatis無法自動完成映射關系。
此時有兩種解決方案:
-
Sql語句的查詢字段起與POJO屬性相同的別名。
<select id="findAll" resultType="com.mybatis.pojo.Teacher"> select tid as id,tname as teacherName from teacher; </select> -
自定義映射關系
- 在映射文件中,使用
<resultMap>自定義映射關系:
<!-- id:自定義映射名 type:自定義映射的對象類型 --> <resultMap id="teacherMapper" type="com.mybatis.pojo.Teacher"> <!-- id定義主鍵列 property:POJO屬性名 column:數據庫列名 --> <id property="id" column="tid"></id> <!-- result定義普通列 property:POJO屬性名 column:數據庫列名 --> <result property="teacherName" column="tname"></result> </resultMap>- 在
<select>標簽中,使用resultMap屬性代替resultType屬性,使用自定義映射關系。
<select id="findAll" resultMap="teacherMapper"> select * from teacher </select> - 在映射文件中,使用
< sql>&< include>
<sql>用來定義可重用的Sql片段,通過<include>引入該片段。如:Sql語句的查詢字段起與POJO屬性相同的別名,該Sql片段就可以重用。
<sql id="selectAllField">
select tid as id,tname as teacherName
</sql>
<select id="findAll" resultType="com.mybatis.pojo.Teacher">
<include refid="selectAllField"></include>
from teacher;
</select>
<select id="findById" resultType="com.mybatis.pojo.Teacher">
<include refid="selectAllField"></include>
from teacher where tid = #{id}
</select>
特殊字符處理
在Mybatis映射文件中盡量不要使用一些特殊字符,如:<,>等。
我們可以使用符號的實體來表示:(帶后面的分號)
| 符號 | 實體 |
|---|---|
| < | & lt; |
| > | & gt; |
| & | & amp; |
| ‘ | & apos; |
| “ | & quot; |
如:
<select id="findById2" resultType="com.mybatis.pojo.Teacher">
<include refid="selectAllField"></include>
from teacher where tid > #{id}
</select>
動態SQL

一個查詢的方法的Sql語句不一定是固定的。比如電商網站的查詢商品,用戶使用不同條件查詢,Sql語句就會添加不同的查詢條件。此時就需要在方法中使用動態Sql語句。
< i f > < if> <if>
<if>標簽內的Sql片段在滿足條件后才會添加,用法為:<if test="條件">。例如:根據不同條件查詢用戶:
-
持久層接口添加方法
// 用戶通用查詢 List<User> findByCondition(User user); -
映射文件添加標簽
<select id="findByCondition" parameterType="com.mybatis.pojo.User" resultType="com.mybatis.pojo.User"> select * from user where 1 = 1 <if test="username != null and username.length() != 0"> and username like #{username} </if> <if test="sex != null and sex.length() != 0"> and sex = #{sex} </if> <if test="address != null and address.length() != 0"> and address = #{address} </if> </select> -
編寫測試方法
@Test public void testFindByCondition(){ User user = new User(); List<User> users1 = userMapper2.findByCondition(user); //users1.forEach(System.out::println); user.setUsername("%張三%"); List<User> users2 = userMapper2.findByCondition(user); users2.forEach(System.out::println); user.setAddress("北京"); List<User> users3 = userMapper2.findByCondition(user); users3.forEach(System.out::println); }
if中的條件不能使用&&/||,而應該使用and/or
if中的條件可以直接通過屬性名獲取參數POJO的屬性值,并且該值可以調用方法。
where后為什么要加1=1?
任意條件都可能拼接到Sql中。如果有多個條件,從第二個條件開始前都需要加And關鍵字。加上1=1這個永久成立的條件,就不需要考慮后面的條件哪個是第一個條件,后面的條件前都加And關鍵字即可。
< w h e r e > <where> <where>
<where>可以代替sql中的where 1=1 和第一個and,更符合程序員的開發習慣,使用<where>后的映射文件如下:
<select id="findByCondition" resultType="com.mybatis.user.User" parameterType="com.mybatis.user.User">
select * from user
<where>
<if test="username != null and username.length() != 0">
username like #{username}
</if>
<if test="sex != null and sex.length() != 0">
and sex = #{sex}
</if>
</where>
</select>
< s e t > <set> <set>
<set>標簽用在update語句中。借助<if>,可以只對有具體值的字段進行更新。<set>會自動添加set關鍵字,并去掉最后一個if語句中多余的逗號。
<update id="update" parameterType="com.mybatis.user.User">
update user
<set>
<if test="username != null and username.length() > 0">
username = #{username},
</if>
<if test="sex != null and sex.length() > 0">
sex = #{sex},
</if>
</set>
<where>
id = #{id}
</where>
</update>
< c h o o s e > < choose> <choose>、 < w h e n > < when> <when>、 < o t h e r w i s e > < otherwise> <otherwise>
這些標簽表示多條件分支,類似JAVA中的switch...case。<choose>類似switch,<when>類似case,<otherwise>類似default,用法如下:
<select id="findByCondition" resultType="com.mybatis.user.User" parameterType="com.mybatis.user.User">
select * from user
<where>
<choose>
<when test="username.length() < 5">
username like #{username}
</when>
<when test="username.length() < 10">
username = #{username}
</when>
<otherwise>
id = 1
</otherwise>
</choose>
</where>
</select>
這段代碼的含義為:用戶名<5時使用模糊查詢,用戶名>=5并且<10時使用精確查詢,否則查詢id為1的用戶
< f o r e a c h > <foreach> <foreach>
<foreach>類似JAVA中的for循環,可以遍歷集合或數組。<foreach>有如下屬性:
- collection:遍歷的對象類型
- open:開始的sql語句
- close:結束的sql語句
- separator:遍歷每項間的分隔符
- item:表示本次遍歷獲取的元素,遍歷List、Set、數組時表示每項元素,遍歷map時表示鍵值對的值。
- index:遍歷List、數組時表示遍歷的索引,遍歷map時表示鍵值對的鍵。
遍歷數組
我們使用<foreach>遍歷數組進行批量刪除。
-
持久層接口添加方法
void deleteBatch(int[] ids); -
映射文件添加標簽
<delete id="deleteBatch" parameterType="int"> delete from user <where> <foreach open="id in(" close=")" separator="," collection="array" item="id" > #{id} </foreach> </where> </delete> -
編寫測試方法
@Test public void testDeleteBatch(){ int[] ids = {9,11}; userMapper.deleteBatch(ids); session.commit(); }
遍歷Collection
<foreach>遍歷List和Set的方法是一樣的,我們使用<foreach>遍歷List進行批量添加。
-
持久層接口添加方法
void insertBatch(List<User> users); -
映射文件添加標簽
<insert id="insertBatch" parameterType="com.mybatis.user.User"> insert into user values <foreach collection="list" item="user" separator=","> (null ,#{user.username},#{user.sex},#{user.address}) </foreach> </insert> -
編寫測試方法
@Test public void testInsertBatch(){ User user1 = new User("程序員1", "男", "北京"); User user2 = new User("程序員2", "女", "上海"); List<User> users = new ArrayList(); users.add(user1); users.add(user2); userMapper2.insertBatch(users); session.commit(); }
遍歷Map
我們使用<foreach>遍歷Map進行多條件查詢。
-
持久層接口添加方法
/** * 多條件查詢 * @param map 查詢的條件鍵值對 鍵:屬性名 值:屬性值 * @return */ List<User> findUser(@Param("queryMap") Map<String,Object> map); -
映射文件添加標簽
<select id="findUser" parameterType="map" resultType="com.mybatis.pojo.User"> select * from user <where> <foreach collection="queryMap" separator="and" index="key" item="value"> ${key} = #{value} </foreach> </where> </select> -
編寫測試方法
@Test public void testFindUser(){ Map<String,Object> queryMap = new HashMap(); queryMap.put("sex","男"); queryMap.put("address","北京"); List<User> users = userMapper2.findUser(queryMap); users.forEach(System.out::println); }
MyBatis緩存

緩存介紹
緩存是內存當中一塊存儲數據的區域,目的是提高查詢效率。MyBatis會將查詢結果存儲在緩存當中,當下次執行相同的SQL時不訪問數據庫,而是直接從緩存中獲取結果,從而減少服務器的壓力。
-
什么是緩存?
存在于內存中的一塊數據。
-
緩存有什么作用?
減少程序和數據庫的交互,提高查詢效率,降低服務器和數據庫的壓力。
-
什么樣的數據使用緩存?
經常查詢但不常改變的,改變后對結果影響不大的數據。
-
MyBatis緩存分為哪幾類?
一級緩存和二級緩存
-
如何判斷兩次Sql是相同的?
- 查詢的Sql語句相同
- 傳遞的參數值相同
- 對結果集的要求相同
- 預編譯的模板Id相同
MyBatis一級緩存
- MyBatis一級緩存也叫本地緩存。SqlSession對象中包含一個Executor對象,Executor對象中包含一個PerpetualCache對象,在該對象存放一級緩存數據。
- 由于一級緩存是在SqlSession對象中,所以只有使用同一個SqlSession對象操作數據庫時才能共享一級緩存。
- MyBatis的一級緩存是默認開啟的,不需要任何的配置。
測試一級緩存
@Test
public void testCache1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
// 使用同一個SqlSession查詢
UserMapper mapper1 = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
System.out.println("-------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
}
@Test
public void testCache2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
// 使用不同的SqlSession查詢
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
System.out.println("-------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
}
MyBatis清空一級緩存
進行以下操作可以清空MyBatis一級緩存:
SqlSession調用close():操作后SqlSession對象不可用,該對象的緩存數據也不可用。SqlSession調用clearCache()/commit():操作會清空一級緩存數據。SqlSession調用增刪改方法:操作會清空一級緩存數據,因為增刪改后數據庫發生改變,緩存數據將不準確。
@Test
public void testCache3() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
UserMapper mapper1 = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
// session.close();
// session.clearCache();
// session.commit();
mapper1.delete(2);
System.out.println("-------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
}
MyBatis二級緩存
-
MyBatis二級緩存也叫全局緩存。數據存放在SqlSessionFactory中,只要是同一個工廠對象創建的SqlSession,在進行查詢時都能共享數據。一般在項目中只有一個SqlSessionFactory對象,所以二級緩存的數據是全項目共享的。
-
MyBatis一級緩存存放的是對象,二級緩存存放的是對象的數據。所以要求二級緩存存放的POJO必須是可序列化的,也就是要實現Serializable接口。
-
MyBatis二級緩存默認不開啟,手動開啟后數據先存放在一級緩存中,只有一級緩存數據清空后,數據才會存到二級緩存中。
SqlSession調用clearCache()無法將數據存到二級緩存中。
開啟二級緩存
-
POJO類實現Serializable接口。
public class User implements Serializable { private int id; private String username; private String sex; private String address; } -
在MyBatis配置文件添加如下設置:
<settings> <setting name="cacheEnabled" value="true"/> </settings>由于cacheEnabled默認值是true,所以該設置可以省略。
-
在映射文件添加
<cache />標簽,該映射文件下的所有方法都支持二級緩存。如果查詢到的集合中對象過多,二級緩存只能緩存1024個對象引用。可以通過
<cache />標簽的size屬性修改該數量。<cache size="2048"/> -
測試二級緩存
@Test public void testCache4() throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); SqlSession session1 = factory.openSession(); SqlSession session2 = factory.openSession(); UserMapper mapper1 = session1.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user1 = mapper1.findById(1); System.out.println(user1); System.out.println(user1.hashCode()); // 讓一級緩存失效 session1.commit(); System.out.println("-------------------------------------------"); User user2 = mapper2.findById(1); System.out.println(user2); System.out.println(user2.hashCode()); }
浙公網安備 33010602011771號