MyBatis框架——動態SQL、緩存機制、逆向工程

MyBatis框架——動態SQL、緩存機制、逆向工程

1、Dynamic SQL

爲何須要動態SQL?有時候須要根據實際傳入的參數來動態的拼接SQL語句。
最經常使用的就是:where和if標籤
java

1.參考官方文檔

if:字符判斷
choose (when, otherwise):分支選擇
trim (where, set):字符串截取;其中where標籤封裝查詢條件,set標籤封裝修改條件
foreach:遍歷,實現批處理
mysql

2.if案例:

1)在EmployeeMapper接口中添加一個方法:
//攜帶了哪一個字段,查詢條件就帶上哪一個字段的值
public List<Employee> getEmployeeByConditionIf(Employee employee);

  

2).若是要寫下列的SQL語句,只要是不爲空,就做爲查詢條件,以下所示,這樣寫其實是有問題的,因此咱們要寫成動態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標籤改寫爲動態SQL,以下所示:

<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee">
  select *from tbl_employee 
  where 
  <!--
    test:判斷表達式(OGNL)
    OGNL參照PPT或者官方文檔。
    c:if test
    從參數中取值進行判斷
    碰見特殊符號,應該去寫轉義字符:參考W3CSchool>>HTML>>ISO8859
  -->
  <if test="id != null">
    id = #{id}
  </if>
  <if test="userName != null && userName !=''">
    and user_name = #{userName} 
  </if>
  <if test="email != null and email.trim() != """>
    and email = #{email}
  </if>
  <!-- ognl會進行字符串和數字的轉換判斷;"0"==0,"1"==1 -->
  <if test="gender == 0 or gender == 1">
    and gender = #{gender} 
  </if>
</select>

  

4).測試代碼:

@Test
public void testGetEmployee(){
  EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
  Employee employee = new Employee();
  employee.setId(1);
  employee.setUserName("張三丰");
  employee.setEmail("sunwukong@163.com");
  employee.setGender(1);
  List<Employee> list = mapper.getEmployeeByConditionIf(employee);
  System.out.println(list);
}

測試結果沒問題 。

可是仔細來講,上面的sql語句是有問題的,當咱們不給動態sql語句傳遞id值的時候,sql語句的拼裝就會有問題! sql

解決辦法:

1.給where後面加上1=1,之後的條件均可以使用and xxx了
2.mybatis可使用where標籤來將全部的查詢條件包括在內。mybatis就會將where標籤中拼裝的sql,多出來的and或者or去掉!數據庫

 

//須要注意:where標籤只會去掉第一個多出來的and或者orapi

3.也就是說使用where標籤有時候仍是不能解決問題的,那怎麼辦呢?咱們這裏可使用trim標籤!緩存

2.trim標籤:能夠自定義字符串的截取規則

<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee">
  select *from tbl_employee
  <!-- 
    後面多出的and或者or where標籤不可以解決
    prefix="":前綴:trim標籤體重是整個字符串拼串後的結果。
    prefix給拼串後的整個字符串加一個前綴
    prefixOverrides="":
    前綴覆蓋:去掉整個字符串前面多餘的字符
    suffix="":後綴
    suffix給拼串後的整個字符串加一個後綴
    suffixOverrides="":
    後綴覆蓋:去掉整個字符串後面多餘的字符
  --> 
  <trim prefix="where" suffixOverrides="and">
    <if test="id != null">
      id = #{id} and
    </if>
    <if test="userName != null && userName !=''">
      user_name = #{userName} and 
    </if>
    <if test="email != null and email.trim() != """>
      email = #{email} and 
    </if>
    <!-- ognl會進行字符串和數字的轉換判斷;"0"==0,"1"==1 -->
    <if test="gender==0 or gender==1">
      gender = #{gender}
    </if>	
  </trim>
</select>

  

3.choose標籤:分支選擇,相似於Java中的帶了break的switch...case

choose (when, otherwise):若是帶了id,就用id查,若是帶了userName就用userName查,只會進入其中一個!

案例演示:安全

1).在EmployeeMapper接口中添加一個方法:

public List<Employee> getEmployeeByConditionChoose(Employee employee);


2).sql映射文件

<!-- public List<Employee> getEmployeeByConditionChoose(Employee employee); -->
<select id="getEmployeeByConditionChoose" resultType="com.neuedu.entity.Employee">
  select *from tbl_employee
  <where>
    <!-- 若是帶了id,就用id查,若是帶了userName就用userName查,只會進入其中一個! -->
    <choose>
    <when test="id != null">
      id = #{id}
    </when>
    <when test="userName != null">
      user_name like #{userName}
    </when>
    <when test="email != null">
      email = #{email}
    </when>
    <otherwise>
      1=1
    </otherwise>
    </choose>
  </where>
</select>

  

4.trim 中的set標籤(where, set):字符串截取;其中where標籤封裝查詢條件,set標籤封裝修改條件

set元素會動態前置set關鍵字,同時也會消除無關的逗號。
session

1).在EmployeeMapper中添加一個更新的方法,以下所示:

public void updateEmp(Employee employee);

  

2).在sql映射文件中,填寫相應的sql語句,以下所示【set標籤能夠將字段後面的逗號去掉】:

<update id="updateEmp">
  update tbl_employee 
  <set>
    <if test="userName != null">
      user_name = #{userName},
    </if>
    <if test="email != null">
      email = #{email},
    </if>
    <if test="gender != null">
      gender = #{gender},
    </if>
  </set>
  where id = #{id}
</update>

  

3).測試類代碼爲:

@Test
public void testGetEmployee(){
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
employee.setId(1);
employee.setUserName("哈哈");
employee.setEmail("sunwukong@163.com");
employee.setGender(1);
mapper.updateEmp(employee);
}

 

4).固然上面的set標籤咱們也可使用trim標籤來代替,以下所示:

<update id="updateEmp">
update tbl_employee 
<trim prefix="set" suffixOverrides=",">
<if test="userName != null">
user_name = #{userName},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="gender != null">
gender = #{gender},
</if>
</trim>
where id = #{id}
</update>

  

5.foreach:遍歷元素

動態SQL的另外一個經常使用的操做是須要對一個集合進行遍歷,一般在構建in條件語句的時候!
foreach元素容許指定一個集合,聲明集合項和索引變量,並能夠指定開閉匹配的字符串以及在迭代之間放置分隔符。

案例演示:mybatis

1).在EmployeeMapper接口中加入一個方法,以下所示:

public List<Employee> getEmpsByConditionForeach(@Param("ids") List<Integer> ids);


2).在MyBatis的sql映射文件中寫相應的代碼:

<!-- public List<Employee> getEmpsByConditionForeach(List<Integer> ids); -->
<select id="getEmpsByConditionForeach" resultType="com.neuedu.entity.Employee">
  select * from tbl_employee where id in
  <!-- 
    collection:指定要遍歷的集合
    item:將當前遍歷出的元素賦值給指定的變量
    separator:每一個元素之間的分隔符
    open:遍歷出全部記過拼接一個開始的字符
    close:遍歷出全部結果拼接一個結束的字符
  -->
  <foreach collection="ids" open="(" close=")" separator="," item="id">
    #{id}
  </foreach>
</select>


3).測試類代碼爲:

@Test
public void testGetEmployee(){
  EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
  List<Integer> asList = Arrays.asList(1,2,3,4);
  List<Employee> emps = mapper.getEmpsByConditionForeach(asList);
  for (Employee employee : emps) {
    System.out.println(employee);
  }
}

  

foreach標籤還能夠用於批量保存數據,以下所示:
app

1).在EmployeeMapper接口類中添加批量插入的方法:

public void addEmps(@Param("emps") List<Employee> emps);

  

2).在EmployeeMapper.xml的sql映射文件中添加響應的語句:

<!-- public void addEmps(@Param("emps") List<Employee> emps); -->
<!-- MySQL下批量保存:能夠foreach遍歷,mysql支持values(),(),()語法 -->
<insert id="addEmps">
INSERT INTO tbl_employee(user_name,gender,email,d_id) VALUES
<foreach collection="emps" item="emp" separator=",">
(#{emp.userName},#{emp.gender},#{emp.email},#{emp.depart.id})
</foreach>
</insert>

  

3).測試代碼:

@Test
public void testGetEmployee(){
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
List<Employee> emps = new ArrayList<Employee>();
emps.add(new Employee(0, 1, "allen", "allen@163.com", new Department(1)));
emps.add(new Employee(0, 0, "tom", "tom@163.com", new Department(2)));
emps.add(new Employee(0, 1, "mux", "mux@163.com", new Department(1)));
mapper.addEmps(emps);
}

 

 


2、MyBatis-緩存機制

MyBatis 包含一個很是強大的查詢緩存特性,它能夠很是方便地配置和定製。緩存能夠極大的提高查詢效率。
MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存。

一級緩存(本地緩存):

SqlSession級別的緩存,一級緩存是一致開啓的,無法關閉。方法之間不共用!
與數據庫同一次會話期間查詢到的數據放在本地緩存中。
之後若是須要獲取相同的數據,直接從緩存中拿,不必再去查詢數據庫;

 

二級緩存(全局緩存):

–一、默認狀況下,只有一級緩存(SqlSession級別的緩存,也稱爲本地緩存)開啓。
–二、二級緩存須要手動開啓和配置,他是基於namespace級別的緩存。
–三、爲了提升擴展性。MyBatis定義了緩存接口Cache。咱們能夠經過實現Cache接口來自定義二級緩存。

 

案例:

測試一級緩存【默認是開啓的本地緩存的】:

@Test
public void testGetEmployee(){
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);

Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
Employee emp2 = mapper.getEmployeeById(2);
System.out.println(emp2);

System.out.println(emp == emp2);
}

  

一級緩存失效的狀況【4種】(沒有使用到當前一級緩存的狀況,效果就是,還須要再向數據庫發出查詢):

1.sqlSession不一樣。

@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);

SqlSession openSession2= sessionFactory.openSession();
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp2 = mapper2.getEmployeeById(2);
System.out.println(emp2);

System.out.println(emp == emp2);

openSession.close();
openSession2.close();
}


2.SqlSession相同,可是查詢條件不同[當前緩存中尚未這個數據]

@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);

Employee emp2 = mapper.getEmployeeById(3);
System.out.println(emp2);	
System.out.println(emp == emp2);
openSession.close();
}

  

3.SqlSession相同,可是兩次查詢之間執行了增刪改操做【此次增刪改可能對當前數據有影響】。

@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
mapper.updateEmp(new Employee(1, 1, "張三丰","zhangsanfeng@163.com", new Department(1)));
Employee emp2 = mapper.getEmployeeById(2);
System.out.println(emp2);	
System.out.println(emp == emp2);
openSession.close();
}

  

4.SqlSession相同,手動清除了一級緩存[緩存清空]。

@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
//手動清空緩存
openSession.clearCache();
Employee emp2 = mapper.getEmployeeById(2);
System.out.println(emp2);	
System.out.println(emp == emp2);
openSession.close();
}


二級緩存:

【全局緩存】:基於namespace級別的緩存:一個namespace對應一個二級緩存。
【一級緩存的範圍仍是過小了,每次SqlSession一關閉,一級緩存中的數據就消失】
因此從這個角度講:能跨sqlSession的緩存爲二級緩存!

工做機制:

一、一個會話,查詢一條數據,這個數據就會被放在當前會話的一級緩存中。
2.若是會話關閉,一級緩存中的數據會被保存到二級緩存中;新的會話查詢信息,就能夠參照二級緩存中。
3.SqlSession === EmployeeMapper ===> Employee
DepartmentMapper ===> Department
不一樣namespace查出的數據會放在本身對應的緩存中(map)
效果:數據會從二級緩存中獲取
查出的數據都會被默認先放在一級緩存中。
只有會話提交或者關閉以後,一級緩存中的數據纔會轉移到二級緩存中。
須要注意的是:在哪一個Mapper.xml文件中開啓了<cache>緩存標籤,哪一個Mapper中就開啓了二級緩存。

使用:

1).在MyBatis全局配置文件中開啓全局二級緩存配置:

<setting name="cacheEnabled" value="true"/>

 

2).去mapper.xml中配置使用二級緩存:

<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024" type=""></cache>
<!-- 
eviction=「FIFO」:緩存回收策略:
LRU –最近最少使用的:移除最長時間不被使用的對象。
FIFO –先進先出:按對象進入緩存的順序來移除它們。
SOFT –軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。
WEAK –弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。
默認的是LRU。
flushInterval:緩存刷新間隔
緩存多長時間清空一次,默認不清空,設置一個毫秒值。
size:引用數目,正整數
表明緩存最多能夠存儲多少個對象,太大容易致使內存溢出
readOnly:是否只讀,true/false	
true:只讀緩存;mybatis認爲全部從緩存中獲取數據的操做都是隻讀操做,不會修改數據。
mybatis爲了加快獲取速度,直接就會將數據在緩存中的引用交給用戶。不安全,速度快。
false:非只讀:mybatis以爲獲取的數據可能會被修改。
mybatis會利用序列化&反序列化的技術克隆一份。安全,速度慢。
type:指定自定義緩存的全類名
實現cache接口便可!
-->

 

3).咱們的POJO須要實現序列化接口[implements Serializable]

測試二級緩存【測試代碼】:

@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
//開啓兩個會話
SqlSession openSession= sessionFactory.openSession();
SqlSession openSession2 = sessionFactory.openSession();
//利用兩個openSession對象獲取兩個mapper對象
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
//用第一個openSession獲取的mapper對象查詢2號員工信息
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
//關閉第一個openSession對象
openSession.close();
//用第二個openSession獲取的mapper對象查詢2號員工信息
Employee emp2 = mapper2.getEmployeeById(2);
System.out.println(emp2);	
openSession2.close();

}

//能夠看到只發送了一次SQL語句,第二次查詢時從二級緩存中拿到的數據,並無發送新的sql語句。
//須要注意的是:只有一級緩存中關閉的狀況下,二級緩存纔會被使用。

須要注意的是:在哪一個Mapper.xml文件中開啓了<cache>緩存標籤,哪一個Mapper中就開啓了二級緩存。可用DepartmentMapper.xml驗證


和緩存有關的設置/屬性:
1)cacheEnabled="true": false:關閉緩存(二級緩存關閉)【一級緩存一直可用】
2)每一個select標籤都有useCache="true";
false:不使用緩存(一級緩存依然使用,二級緩存不使用)
3)每一個增刪改標籤都有一個flushCache="true":增刪改執行完成後就會清楚緩存【一級二級緩存都會被清空】
查詢標籤:flushCache = "false"
若是flushCache = true;每次查詢以前都會清空緩存,緩存是沒有被使用!

3、MyBatis逆向工程:

1.是什麼?

  MyBatis官方提供的逆向工程,能夠將單表生成經常使用的Mapper、Entity等配置

2.能幹嗎?

  從數據庫表反向生成mapper.java/mapper.xml/entity/輔助查詢類

3.怎麼玩?

(1)導入逆向工程jar包

mybatis-3.2.3.jar
mybatis-generator-core-1.3.2.jar
mysql-connector-java-5.1.28-bin.jar

(2)導入逆向工程配置文件(generatorConfig.xml),直接放在項目名下,並修改信息:

<?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>

	<!-- 無Example等內容(本身選擇有無Example) -->
	<context id="Mysql" targetRuntime="MyBatis3Simple"
		defaultModelType="flat">


		<!-- 有Example查詢條件內容 -->
		<!-- <context id="testTables" targetRuntime="MyBatis3"> -->
		<commentGenerator>
			<!-- 是否去除自動生成的註釋 true:是 : false:否 -->
			<property name="suppressAllComments" value="true" />
		</commentGenerator>

		<!--數據庫鏈接的信息:驅動類、鏈接地址、用戶名、密碼 (本身修改)-->
		<jdbcConnection 
			driverClass="com.mysql.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/user" 
			userId="root"
			password="123456">
		</jdbcConnection>

		<!-- 默認false,把JDBC DECIMAL 和 NUMERIC 類型解析爲 Integer,爲 true時把JDBC DECIMAL 
			和 NUMERIC 類型解析爲java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成Entity類的路徑 -->
		<javaModelGenerator targetProject=".\src"
			targetPackage="com.neuedu.mybatis.entities">
			<!-- enableSubPackages:是否讓schema做爲包的後綴 -->
			<property name="enableSubPackages" value="false" />
			<!-- 從數據庫返回的值被清理先後的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>

		<!-- targetProject:XXXMapper.xml映射文件生成的路徑 -->
		<sqlMapGenerator targetProject=".\src"
			targetPackage="com.neuedu.mybatis.mapper">
			<!-- enableSubPackages:是否讓schema做爲包的後綴 -->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>

		<!-- targetPackage:Mapper接口生成的位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetProject=".\src" targetPackage="com.neuedu.mybatis.mapper">
			<!-- enableSubPackages:是否讓schema做爲包的後綴 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>

		<!-- 數據庫表名字和咱們的entity類對應的映射指定(需本身修改) -->
		<table tableName="person" domainObjectName="Person" />
		<table tableName="stu" domainObjectName="Stu" />

		<!-- 有些表的字段須要指定java類型 <table schema="" tableName=""> <columnOverride column="" 
			javaType="" /> </table> -->
	</context>
</generatorConfiguration>

(3)導入Mbg_GeneratorUtil.java類(不須要修改):

package com.neuedu.mybatis.mbg;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class Mbg_GeneratorUtil
{
	public void generator() throws Exception
	{
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		//指定 逆向工程配置文件
		File configFile = new File("generatorConfig.xml"); 
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(configFile);
		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,callback, warnings);
		myBatisGenerator.generate(null);

	} 
	
	public static void main(String[] args) throws Exception 
	{
		try 
		{
			new Mbg_GeneratorUtil().generator();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
} 

(4)直接運行程序,而後在項目名點擊右鍵——>刷新,就會出現生成的包、類和mapper配置文件

相關文章
相關標籤/搜索