MyBatis之動(dòng)態(tài)SQL和緩存使用及說(shuō)明
前言
在這篇文章中荔枝將會(huì)梳理有關(guān)MyBatis動(dòng)態(tài)SQL和MyBatis緩存的相關(guān)知識(shí),同時(shí)也稍微了解了有關(guān)MyBatis中借助MAVEN中的插件管理來(lái)實(shí)現(xiàn)逆向工程。
一、動(dòng)態(tài)SQL
Mybatis框架的動(dòng)態(tài)SQL技術(shù)是一種根據(jù)特定條件動(dòng)態(tài)拼裝SQL語(yǔ)句的功能,它存在的意義是為了解決拼接SQL語(yǔ)句字符串時(shí)的問(wèn)題。
在執(zhí)行SQL查詢時(shí),只有屬性值存在我們才能做相應(yīng)的SQL語(yǔ)句的拼接,因此我們需要?jiǎng)討B(tài)來(lái)拼裝SQL語(yǔ)句。
在JDBC中我們根據(jù)不同的條件拼接SQL語(yǔ)句時(shí)往往需要注意一些預(yù)留處理,而MyBatis的動(dòng)態(tài)SQL特性就極大地簡(jiǎn)化了這一處理過(guò)程。
1.1 if標(biāo)簽
根據(jù)if標(biāo)簽中的test屬性所對(duì)應(yīng)的表達(dá)式?jīng)Q定標(biāo)簽中的內(nèi)容是否需要拼接到SQL中。
<?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.crj.mybatis.mapper.DynamicSQLMapper">
<!-- List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp where 1=1
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
</select>
</mapper>這段實(shí)例demo中,我們需要在select語(yǔ)句中添加一個(gè)恒成立的條件1=1,這是為了防止SQL語(yǔ)句在某些屬性不存在時(shí)出現(xiàn)的拼接錯(cuò)誤。動(dòng)態(tài)SQL的特性其實(shí)類似一種按需加載的過(guò)程,彈性拼接起SQL語(yǔ)句。if標(biāo)簽中的test屬性其實(shí)就是if標(biāo)簽中的SQL語(yǔ)句段拼接的條件,滿足這個(gè)條件才能拼接在原有的查詢SQL語(yǔ)句中。
1.2 where標(biāo)簽
where標(biāo)簽可以輔助我們動(dòng)態(tài)生成SQL語(yǔ)句中的where關(guān)鍵字,當(dāng)where后面有內(nèi)容時(shí),where標(biāo)簽會(huì)幫助我們自動(dòng)生成where關(guān)鍵字,同時(shí)會(huì)去掉內(nèi)容前多余的and和or關(guān)鍵字,此時(shí)就無(wú)需我們手動(dòng)添加where關(guān)鍵字和恒成立的條件了;當(dāng)where后面沒(méi)有內(nèi)容時(shí),此時(shí)不會(huì)生成where關(guān)鍵字。
<!-- List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<where>
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
</where>
</select>1.3 trim標(biāo)簽
若標(biāo)簽中有內(nèi)容時(shí),屬性功能正常;若標(biāo)簽沒(méi)有內(nèi)容時(shí), trim不會(huì)有任何效果。
幾個(gè)屬性:
- prefix:將trim標(biāo)簽中內(nèi)容前面添加指定內(nèi)容
- suffix:將trim標(biāo)簽中內(nèi)容后面添加指定內(nèi)容
- prefixOverrides:將trim標(biāo)簽中內(nèi)容前面去掉指定內(nèi)容
- suffixOverrides:將trim標(biāo)簽中內(nèi)容后面去掉指定內(nèi)容
<!-- List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null and empName != ''">
emp_name = #{empName} and
</if>
<if test="age != null and age != ''">
age = #{age} and
</if>
<if test="sex != null and sex != ''">
sex = #{sex}
</if>
</trim>
</select>1.4 choose、when、otherwise標(biāo)簽
choose和when標(biāo)簽組合起來(lái)相當(dāng)于if...else if...else的結(jié)構(gòu),如果所有的when條件都不滿足,則會(huì)執(zhí)行otherwise中的條件。
<!-- List<Emp> getEmpByChoose(Emp emp);-->
<select id="getEmpByChoose" resultType="Emp">
select * from t_emp
<where>
<choose>
<when test="empName != null and empName != ''">
emp_name = #{empName}
</when>
<when test="age != null and age != ''">
age = #{age}
</when>
<when test="sex != null and sex != ''">
sex = #{sex}
</when>
<otherwise>
did = 1
</otherwise>
</choose>
</where>
</select>1.5 foreach標(biāo)簽
foreach標(biāo)簽簡(jiǎn)單來(lái)說(shuō)就是一個(gè)循環(huán),可以被用來(lái)執(zhí)行批量操作,foreach標(biāo)簽有五種屬性:
- collection:設(shè)置需要循環(huán)的數(shù)組或集合
- item:循環(huán)出來(lái)的單個(gè)對(duì)象
- sqparator:循環(huán)體之間的分隔符
- open:循環(huán)的所有內(nèi)容開(kāi)頭的內(nèi)容
- close:循環(huán)的所有內(nèi)容結(jié)束的內(nèi)容
<!-- int deleteMoreByArray(Integer[] eids);-->
<delete id="deleteMoreByArray">
delete * from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>1.5 sql標(biāo)簽
sql標(biāo)簽又成為SQL片段,它可以用來(lái)記錄常用的sql執(zhí)行語(yǔ)句。通過(guò)include標(biāo)簽來(lái)引用SQL片段。
<sql id="emColumns">eid,emp_name,age,sex,email</sql>
<select id="getEmpByChoose" resultType="Emp">
select <include refid="emColumns"></include> from t_emp
</select>二、MyBatis的緩存
2.1 一級(jí)緩存
MyBatis的一級(jí)緩存是SqlSession級(jí)別的,通過(guò)同一個(gè)SqlSession查詢的數(shù)據(jù)會(huì)被緩存,下次查詢相同的數(shù)據(jù)就會(huì)從緩存中直接獲取而不會(huì)從數(shù)據(jù)庫(kù)中重新訪問(wèn)。一級(jí)緩存是默認(rèn)開(kāi)啟的。
一級(jí)緩存失效的四種情況:
- 不同的SqlSession對(duì)應(yīng)不同的一級(jí)緩存
- 同一個(gè)SqlSession但是查詢條件不同
- 同一個(gè)SqlSessioni兩次查詢期間執(zhí)行了任何一次增刪改操作
- 同一個(gè)SqlSession兩次查詢期間手動(dòng)清空了緩存
手動(dòng)清空緩存:
sqlSession.clearCache();
2.2 二級(jí)緩存
二級(jí)緩存是SqlSessionFactory級(jí)別,通過(guò)同一個(gè)SqlSessionFactory創(chuàng)建的SqlSessioni查詢的結(jié)果會(huì)被緩存;此后若再次執(zhí)行相同的查詢語(yǔ)句,結(jié)果就會(huì)從緩存中獲取。
二級(jí)緩存開(kāi)啟的條件:
- 在核心配置文件中,設(shè)置全局配置屬性cacheEnabled="true",默認(rèn)為true,不需要設(shè)置
- 在映射文件中設(shè)置標(biāo)簽<cache />
- 二級(jí)緩存必須在SqlSession關(guān)閉或提交之后有效 sqlSession.close() | sqlSession.commit()
- 查詢的數(shù)據(jù)所轉(zhuǎn)換的實(shí)體類類型必須實(shí)現(xiàn)序列化的接口
實(shí)體類實(shí)現(xiàn)序列化接口
public class Emp implements Serializable
二級(jí)緩存失效的情況:
兩次查詢之間執(zhí)行了任意的增刪改,會(huì)使一級(jí)和二級(jí)緩存同時(shí)失效。
二級(jí)緩存的相關(guān)配置
在二級(jí)緩存的開(kāi)啟條件中,我們發(fā)現(xiàn)<cache />標(biāo)簽是可以配置一些屬性的,具體如下
eviction屬性:緩存回收策略
- LRU(Least Recently Used)-最近最少使用的:移除最長(zhǎng)時(shí)間不被使用的對(duì)象。
- FIFO(First in First out)-先進(jìn)先出:按對(duì)象進(jìn)入緩存的順序來(lái)移除它們。
- SOFT-軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對(duì)象。
- WEAK-弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對(duì)象。
默認(rèn)的是LRU。
flushlnterval屬性:刷新間隔,單位毫秒
默認(rèn)情況是不設(shè)置,也就是沒(méi)有刷新間隔,緩存僅僅調(diào)用增刪改語(yǔ)句時(shí)刷新
size屬性:引用數(shù)目,正整數(shù)
代表緩存最多可以存儲(chǔ)多少個(gè)對(duì)象,太大容易導(dǎo)致內(nèi)存溢出
readOnly屬性:只讀,true/false
- true:只讀緩存;會(huì)給所有調(diào)用者返回緩存對(duì)象的相同實(shí)例。因此這些對(duì)象不能被修改。這提供了很重要的性能優(yōu)勢(shì)。
- false:讀寫(xiě)緩存;會(huì)返回緩存對(duì)象的拷貝(通過(guò)序列化)。這會(huì)慢一些,但是安全,因此默認(rèn)是flse。
2.3 MyBatis緩存查詢順序
首先先查詢二級(jí)緩存,因?yàn)槎?jí)緩存中可能會(huì)有其他程序已經(jīng)查出來(lái)的數(shù)據(jù),可以拿來(lái)直接使用。
如果二級(jí)緩存沒(méi)有命中,再查詢一級(jí)緩存;如果一級(jí)緩存也沒(méi)有命中,則查詢數(shù)據(jù)庫(kù)。
注意在sqlSession關(guān)閉之后,一級(jí)緩存中的數(shù)據(jù)會(huì)寫(xiě)入二級(jí)緩存
三、MyBatis的逆向工程(MBG)
正向工程:先創(chuàng)建ava實(shí)體類,由框架負(fù)責(zé)根據(jù)實(shí)體類生成數(shù)據(jù)庫(kù)表。Hibernate是支持正向工程的。
逆向工程:先創(chuàng)建數(shù)據(jù)庫(kù)表,由框架負(fù)責(zé)根據(jù)數(shù)據(jù)庫(kù)表,反向生成如下資源:
- Java實(shí)體類
- Mapper接口
- MapperB映射文件
在使用逆向工程之前首先應(yīng)該配置好項(xiàng)目依賴
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.crj</groupId>
<artifactId>MyBatis_MBG</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--依賴MyBatis核心包-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--mysql驅(qū)動(dòng)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<!--log4j日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!--控制Maven在構(gòu)建過(guò)程中相關(guān)配置-->
<build>
<!--構(gòu)建過(guò)程用到的插件-->
<plugins>
<!--具體插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!--插件的依賴-->
<dependencies>
<!--逆向工程的核心依賴-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!--數(shù)據(jù)庫(kù)連接池c3p0-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!--mysql驅(qū)動(dòng)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>逆向工程配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime:執(zhí)行生成的逆向工程的版本
MyBatis3Simple:生成基本的CRUD
MyBatis:生成帶條件的CRUD
-->
<!-- 是否去除自動(dòng)生成的注釋 true:是 : false:否 -->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 數(shù)據(jù)庫(kù)連接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="123456">
</jdbcConnection>
<!--
javaBean的生成策略
生成model模型,對(duì)應(yīng)的包路徑,以及文件存放路徑(targetProject),targetProject可以指定具體的路徑
也可以使用“MAVEN”來(lái)自動(dòng)生成,這樣生成的代碼會(huì)在target/generatord-source目錄下
-->
<javaModelGenerator targetPackage="com.crj.mybatis.pojo" targetProject=".\src\main\java">
<!--是否能夠使用子包-->
<property name="enableSubPackages" value="true" />
<!-- 從數(shù)據(jù)庫(kù)返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--
SQL映射文件的生成策略
對(duì)應(yīng)的mapper.xml文件
-->
<sqlMapGenerator targetPackage="com.crj.mybatis.mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 對(duì)應(yīng)的Mapper接口類文件的生成策略 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.crj.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>測(cè)試類中我們這樣來(lái)進(jìn)行條件查詢:
EmpExample example = new EmpExample();
//這里可以借助example.createCriteria().and開(kāi)頭的方法來(lái)設(shè)置查詢條件
example.createCriteria().andEmpNameEqualTo("荔枝");
mapper.selectByExample(example);
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Jar啟動(dòng)實(shí)現(xiàn)從指定目錄中讀取配置文件過(guò)程
本文介紹了Spring Boot項(xiàng)目中如何將配置文件外置,不在JAR包中打包,而是從指定目錄加載配置文件的方法,通過(guò)修改啟動(dòng)參數(shù)實(shí)現(xiàn)靈活修改配置2026-05-05
SpringBoot實(shí)現(xiàn)固定和動(dòng)態(tài)定時(shí)任務(wù)的三種方法
定時(shí)器是我們項(xiàng)目中經(jīng)常會(huì)用到的,本文主要介紹了SpringBoot實(shí)現(xiàn)固定和動(dòng)態(tài)定時(shí)任務(wù)的三種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
SpringBoot如何配置文件properties和yml
這篇文章主要介紹了SpringBoot如何配置文件properties和yml問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
SpringBoot3實(shí)現(xiàn)統(tǒng)一結(jié)果封裝的示例代碼
Spring Boot進(jìn)行統(tǒng)一結(jié)果封裝的主要目的是提高開(kāi)發(fā)效率、降低代碼重復(fù)率,并且提供一致的API響應(yīng)格式,從而簡(jiǎn)化前后端交互和錯(cuò)誤處理,所以本文給大家介紹了SpringBoot3實(shí)現(xiàn)統(tǒng)一結(jié)果封裝的方法,需要的朋友可以參考下2024-03-03
Spring Eureka 未授權(quán)訪問(wèn)漏洞修復(fù)問(wèn)題小結(jié)
項(xiàng)目組使用的 Spring Boot 比較老,是 1.5.4.RELEASE ,最近被檢測(cè)出 Spring Eureka 未授權(quán)訪問(wèn)漏洞,這篇文章主要介紹了Spring Eureka 未授權(quán)訪問(wèn)漏洞修復(fù)問(wèn)題小結(jié),需要的朋友可以參考下2024-04-04

