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

