有的時候需要根據(jù)要查詢的參數(shù)動態(tài)的拼接SQL語句
永興網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)公司等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)于2013年創(chuàng)立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
常用標(biāo)簽:
- if:字符判斷
- choose【when...otherwise】:分支選擇
- trim【where,set】:字符串截取,其中where標(biāo)簽封裝查詢條件,set標(biāo)簽封裝修改條件
- foreach:
if案例
1)在EmployeeMapper接口文件添加一個方法
public Student getStudent(Student student);
2)如果要寫下列的SQL語句,只要是不為空,就作為查詢條件,如下所示,這樣寫實(shí)際上是有問題的,所以我們要寫成動態(tài)SQL語句:
<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee"> select *from tbl_employee where id = #{id} and user_name = #{userName} and email = #{email} and gender = #{gender} </select>
3)用if標(biāo)簽改寫為動態(tài)SQL,如下所示:
test:判斷表達(dá)式(OGNL):OGNL參照PPT或者官方文檔。
test從參數(shù)中取值進(jìn)行判斷
遇見特殊符號,應(yīng)該去寫轉(zhuǎn)義字符:如<>分別為<,>
<select id="getStudent" resultType="com.neuedu.mybatis.entity.Student"> SELECT * FROM student where <if test="id != null"> id=#{id} </if> <if test="name !=null and name!=''"> and name=#{name} </if> <if test="password !=null and password !=''"> and password=#{password} </if> <if test="email !=null and email !=''"> and email=#{email} </if> </select>
4)測試代碼
@Test public void TestgetStudent(){ StudentMapper bean = ioc.getBean(StudentMapper.class); Student student = new Student(4,"jack", "111", "jack@qq.com"); System.out.println(student); Student student2 = bean.getStudent(student); System.out.println(student2); } #測試結(jié)果沒問題,
但是仔細(xì)來說,上面的sql語句是有問題的,當(dāng)我們不給動態(tài)sql語句傳遞id值的時候,sql語句的拼裝就會有問題!【name前有一個and】
- where 標(biāo)簽
解決辦法
1.給where后面加上1=1,以后的條件都可以使用and xxx了
2.可以使用 where 標(biāo)簽來將所有的查詢條件包括在內(nèi)
mybatis就會將where標(biāo)簽中拼裝的sql,多出來的and或者or去掉!
<select id="getStudent" resultType="com.neuedu.mybatis.entity.Student"> SELECT * FROM student <where> <if test="id != null"> id=#{id} </if> <if test="name !=null and name!=''"> and name=#{name} </if> <if test="password !=null and password !=''"> and password=#{password} </if> <if test="email !=null and email !=''"> and email=#{email} </if> </where> </select>
3.需要注意:where標(biāo)簽只會去掉第一個多出來的and或者or
也就是說使用where標(biāo)簽有時候還是不能解決問題的,那怎么辦呢?我們這里可以使用trim標(biāo)簽!
- trim標(biāo)簽:可以自定義字符串的截取規(guī)則
后面多出的and或者or where標(biāo)簽不能夠解決
prefix="":前綴:trim標(biāo)簽體是整個字符串拼串后的結(jié)果。
prefix給拼串后的整個字符串加一個前綴
prefixOverrides="":前綴覆蓋:去掉整個字符串前面多余的字符
suffix="":后綴
suffix給拼串后的整個字符串加一個后綴
suffixOverrides="":后綴覆蓋:去掉整個字符串后面多余的字符
<select id="getStudent" resultType="com.neuedu.mybatis.entity.Student"> SELECT * FROM student <trim prefix="where" prefixOverrides="and"> <if test="id != null"> id=#{id} </if> <if test="name !=null and name!=''"> and name=#{name} </if> <if test="password !=null and password !=''"> and password=#{password} </if> <if test="email !=null and email !=''"> and email=#{email} </if> </trim> </select>
- choose標(biāo)簽:分支選擇,類似于Java中的帶了break的switch...case
相當(dāng)于確保了第一個case 符合之后,就跳出
案例演示:
1.在EmployeeMapper接口中添加一個方法
public List<Student> getStus(Student student);
2.sql映射文件
<select id="getStus" resultType="com.neuedu.mybatis.entity.Student"> select * from student <where> <choose> <when test="id !=null"> id = #{id} </when> <when test="name !=null and name!=''"> name = #{name} </when> <when test="password !=null and password!=''"> password = #{password} </when> <when test="email !=null and email!=''"> email = #{email} </when> <otherwise> 1 = 1 </otherwise> </choose> </where> </select>
- set標(biāo)簽:字符串截取,可以寫在trim里面
set元素會動態(tài)前置set關(guān)鍵字,同時也會消除無關(guān)的逗號
1)在EmployeeMapper中添加一個更新的方法
public void updateStu(Student student);
2)在sql映射文件中,填寫相應(yīng)的sql語句,如下所示【set標(biāo)簽可以將字段后面的逗號去掉】
<update id="updateStu"> update student <set> <if test="name !=null and name!=''"> name=#{name}, </if> <if test="password !=null and password !=''"> password=#{password}, </if> <if test="email !=null and email !=''"> email=#{email} </if> </set> where id = #{id} </update>
3)測試類代碼為
@Test public void TestUpdateStu(){ StudentMapper bean = ioc.getBean(StudentMapper.class); bean.updateStu(new Student(4, "jackk", null, null)); }
將set標(biāo)簽用trim標(biāo)簽代替
<update id="updateStu"> update student <trim prefix="set" suffixOverrides=","> <if test="name !=null and name!=''"> name=#{name}, </if> <if test="password !=null and password !=''"> password=#{password}, </if> <if test="email !=null and email !=''"> email=#{email} </if> </trim> where id = #{id} </update>
- foreach:遍歷元素
動態(tài)SQL的另一個常用的操作是需要對一個集合進(jìn)行遍歷,通常在構(gòu)建in條件語句的時候!
foreach元素允許指定一個集合,聲明集合項(xiàng)和索引變量,并可以指定開閉匹配的字符串以及在迭代之間放置分隔符。
案例演示:
1.在EmployeeMapper接口中加入一個方法
public List<Student> getStuByIdForEach(@Param("ids")List<Integer> ids);
2.在MyBatis的sql映射文件中寫相應(yīng)的代碼
<select id="getStuByIdForEach" resultType="com.neuedu.mybatis.entity.Student"> select * from student where id in <foreach collection="ids" item="id" open="(" close=")" separator=","> #{id} </foreach> </select>
3.測試類代碼
@Test public void getStuByIdForEach(){ StudentMapper bean = ioc.getBean(StudentMapper.class); List<Integer> list = Arrays.asList(16,17,18,19); List<Student> stuByIdForEachlist = bean.getStuByIdForEach(list); for (Student student : stuByIdForEachlist) { System.out.println(student); } }
foreach標(biāo)簽還可以用于批量保存數(shù)據(jù),
1.在EmployeeMapper接口類中添加批量插入的方法
public void insertStus(@Param("stus")List<Student> student);
2.在EmployeeMapper.xml的sql映射文件中添加響應(yīng)的語句
foreach 中用 collection,collection中是從Mapper接口傳來的參數(shù),separator是去掉中間符號
<insert id="insertStus"> insert into student (name,password,email) values <foreach collection="stus" item="stu" separator=","> (#{stu.name},#{stu.password},#{stu.email}) </foreach> </insert>
3.測試代碼
@Test public void TestInsertStus(){ StudentMapper bean = ioc.getBean(StudentMapper.class); List<Student> list = new ArrayList<Student>(); list.add(new Student("123","123", "123")); list.add(new Student("123","123", "123")); list.add(new Student("123","123", "123")); bean.insertStus(list); }
MyBatis-緩存機(jī)制
MyBatis 包含一個非常強(qiáng)大的查詢緩存特性,它可以非常方便地配置和定制。緩存可以極大的提升查詢效率。
只在MyBatis中,在SSM整合文件中沒用,因?yàn)镾qlSession 定義在 bean.xml中,無法重新定義SqlSession
MyBatis系統(tǒng)中默認(rèn)定義了兩級緩存。
一級緩存和二級緩存
一級緩存:(本地緩存):SqlSession級別的緩存,一級緩存是一直開啟的,沒法關(guān)閉。方法之間不共用!
與數(shù)據(jù)庫同一次會話期間查詢到的數(shù)據(jù)放在本地緩存中。
以后如果需要獲取相同的數(shù)據(jù),直接從緩存中拿,沒必要再去查詢數(shù)據(jù)庫;
二級緩存(全局緩存):
–1、默認(rèn)情況下,只有一級緩存(SqlSession級別的緩存,也稱為本地緩存)開啟。
–2、二級緩存需要手動開啟和配置,他是基于namespace級別的緩存。
–3、為了提高擴(kuò)展性。MyBatis定義了緩存接口Cache。我們可以通過實(shí)現(xiàn)Cache接口來自定義二級緩存。
一級緩存:
案例:測試一級緩存【默認(rèn)是開啟的】
將返回一條select查詢語句,
將返回true,說明emp與emp2是緩存,而不是重新查找
@Test public void TestFirstCache(){ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); session = sqlSessionFactory.openSession(); mapper = session.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmpInfoById(4); System.out.println(emp); Employee emp2 = mapper.getEmpInfoById(4); System.out.println(emp2); System.out.println(emp == emp2); session.commit(); session.close(); }
一級緩存失效的情況【4種】(沒有使用到當(dāng)前一級緩存的情況,效果就是,還需要再向數(shù)據(jù)庫發(fā)出查詢)
1.sqlSession不同,重新定義SqlSession
將返回兩條select語句
將返回false,說明emp2不是emp的緩存
@Test public void TestFirstCache(){ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); session = sqlSessionFactory.openSession(); mapper = session.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmpInfoById(4); System.out.println(emp); SqlSession session2 = sqlSessionFactory.openSession(); EmployeeMapper mapper2 = session2.getMapper(EmployeeMapper.class); Employee emp2 = mapper2.getEmpInfoById(4); System.out.println(emp2); System.out.println(emp == emp2); session.commit(); session.close(); }
2.SqlSession相同,但是查詢條件不一樣[當(dāng)前緩存中還沒有這個數(shù)據(jù)]
就是相當(dāng)于根據(jù)不同條件再次查找
@Test public void TestFirstCache(){ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); session = sqlSessionFactory.openSession(); mapper = session.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmpInfoById(4); System.out.println(emp); Employee emp2 = mapper.getEmpInfoById(16); System.out.println(emp2); System.out.println(emp == emp2); session.commit(); session.close(); }
3.SqlSession相同,但是兩次查詢之間執(zhí)行了增刪改操作【這次增刪改可能對當(dāng)前數(shù)據(jù)有影響】
因?yàn)槟J(rèn)自動刷新了緩存
@Test public void TestFirstCache(){ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); session = sqlSessionFactory.openSession(); mapper = session.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmpInfoById(4); System.out.println(emp); mapper.deleteEmp(16); Employee emp2 = mapper.getEmpInfoById(4); System.out.println(emp2); System.out.println(emp == emp2); session.commit(); session.close(); }
4.SqlSession相同,手動清除了一級緩存[緩存清空]
手動清除了緩存,所以得重新查找
@Test public void TestFirstCache(){ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); session = sqlSessionFactory.openSession(); mapper = session.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmpInfoById(4); System.out.println(emp); session.clearCache(); Employee emp2 = mapper.getEmpInfoById(4); System.out.println(emp2); System.out.println(emp == emp2); session.commit(); session.close(); }
二級緩存:
【全局緩存】:基于namespace級別的緩存:一個namespace對應(yīng)一個二級緩存。
【一級緩存的范圍還是太小了,每次SqlSession一關(guān)閉,一級緩存中的數(shù)據(jù)就消失】
所以從這個角度講:能跨sqlSession的緩存為二級緩存!
工作機(jī)制:
1.一個會話,查詢一條數(shù)據(jù),這個數(shù)據(jù)就會被放在當(dāng)前會話的一級緩存中。
2.如果會話關(guān)閉,一級緩存中的數(shù)據(jù)會被保存到二級緩存中;新的會話查詢信息,就可以參照二級緩存中。
不同namespace查出的數(shù)據(jù)會放在自己對應(yīng)的緩存中(map)
效果:數(shù)據(jù)會從二級緩存中獲取
查出的數(shù)據(jù)都會被默認(rèn)先放在一級緩存中。
只有會話提交或者關(guān)閉之后,一級緩存中的數(shù)據(jù)才會轉(zhuǎn)移到二級緩存中。
需要注意的是:在哪個Mapper.xml文件中開啟了<cache>緩存標(biāo)簽,哪個Mapper中就開啟了二級緩存。
案例:
1)開啟全局二級緩存配置:
<setting name="cacheEnabled" value="true"/>
2)去mapper.xml中配置使用二級緩存
<cache eviction="FIFO" size="100" readOnly="false"/>
其中屬性:
eviction=“FIFO”:緩存回收策略:
LRU –最近最少使用的:移除最長時間不被使用的對象。
FIFO –先進(jìn)先出:按對象進(jìn)入緩存的順序來移除它們。
SOFT –軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象。
WEAK –弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
默認(rèn)的是LRU。
flushInterval:緩存刷新間隔 ?緩存多長時間清空一次,默認(rèn)不清空,設(shè)置一個毫秒值。
size:引用數(shù)目,正整數(shù),默認(rèn)1024
代表緩存最多可以存儲多少個對象,太大容易導(dǎo)致內(nèi)存溢出
readOnly:是否只讀,true/false
true:只讀緩存;mybatis認(rèn)為所有從緩存中獲取數(shù)據(jù)的操作都是只讀操作,不會修改數(shù)據(jù)。
mybatis為了加快獲取速度,直接就會將數(shù)據(jù)在緩存中的引用交給用戶。不安全,速度快。
false:非只讀:mybatis覺得獲取的數(shù)據(jù)可能會被修改。
mybatis會利用序列化&反序列化的技術(shù)克隆一份。安全,速度慢。
type:指定自定義緩存的全類名 實(shí)現(xiàn)cache接口即可!
3)我們的POJO需要實(shí)現(xiàn)序列化接口[implements Serializable]
4)必須先關(guān)閉之前的sqlsession對象
測試:
可以看到只發(fā)送了一次SQL語句,第二次查詢時從二級緩存中拿到的數(shù)據(jù),并沒有發(fā)送新的sql語句。
@Test public void TestFirstCache(){ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); session = sqlSessionFactory.openSession(); mapper = session.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmpInfoById(4); System.out.println(emp); session.close(); SqlSession session2 = sqlSessionFactory.openSession(); EmployeeMapper mapper2 = session2.getMapper(EmployeeMapper.class); Employee emp2 = mapper2.getEmpInfoById(4); System.out.println(emp2); session2.close(); }
需要注意的是:只有一級緩存中關(guān)閉的情況下,二級緩存才會被使用。
需要注意的是:在哪個Mapper.xml文件中開啟了<cache>緩存標(biāo)簽,哪個Mapper中就開啟了二級緩存。
和緩存有關(guān)的設(shè)置/屬性:
1)cacheEnabled="true": false:關(guān)閉緩存(二級緩存關(guān)閉)【一級緩存一直可用】
2)每個select標(biāo)簽都有useCache="true";
false:不使用緩存(一級緩存依然使用,二級緩存不使用)
3)每個增刪改標(biāo)簽都有一個flushCache="true":增刪改執(zhí)行完成后就會清楚緩存【一級二級緩存都會被清空】
查詢標(biāo)簽:flushCache = "false"
如果flushCache = true;每次查詢之前都會清空緩存,緩存是沒有被使用!
總結(jié)
以上所述是小編給大家介紹的MyBatis 動態(tài)SQL和緩存機(jī)制實(shí)例詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對創(chuàng)新互聯(lián)網(wǎng)站的支持!
標(biāo)題名稱:MyBatis動態(tài)SQL和緩存機(jī)制實(shí)例詳解
URL鏈接:http://aaarwkj.com/article2/gihhic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、關(guān)鍵詞優(yōu)化、軟件開發(fā)、ChatGPT、微信小程序、自適應(yīng)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)