MyBatis --- 動態SQL、緩存機制

有的時候須要根據要查詢的參數動態的拼接SQL語句java

經常使用標籤:sql

- if:字符判斷數據庫

- choose【when...otherwise】:分支選擇緩存

- trim【where,set】:字符串截取,其中where標籤封裝查詢條件,set標籤封裝修改條件安全

- foreach:session

 

if案例mybatis

1)在EmployeeMapper接口文件添加一個方法app

public Student getStudent(Student student);

 2)若是要寫下列的SQL語句,只要是不爲空,就做爲查詢條件,以下所示,這樣寫其實是有問題的,因此咱們要寫成動態SQL語句:ide

<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,以下所示:測試

 test:判斷表達式(OGNL):OGNL參照PPT或者官方文檔。
 test從參數中取值進行判斷
 碰見特殊符號,應該去寫轉義字符:如<>分別爲&lt,&gt
<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);
}

 #測試結果沒問題,

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

 

- where 標籤

解決辦法
1.給where後面加上1=1,之後的條件均可以使用and xxx了
2.可使用 where 標籤來將全部的查詢條件包括在內
   mybatis就會將where標籤中拼裝的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標籤只會去掉第一個多出來的and或者or

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

 

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

    後面多出的and或者or where標籤不可以解決
    prefix="":前綴:trim標籤體是整個字符串拼串後的結果。
    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標籤:分支選擇,相似於Java中的帶了break的switch...case

至關於確保了第一個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標籤:字符串截取,能夠寫在trim裏面

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

1)在EmployeeMapper中添加一個更新的方法

public void updateStu(Student student);

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

<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標籤用trim標籤代替

<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:遍歷元素

動態SQL的另外一個經常使用的操做是須要對一個集合進行遍歷,一般在構建in條件語句的時候!
foreach元素容許指定一個集合,聲明集合項和索引變量,並能夠指定開閉匹配的字符串以及在迭代之間放置分隔符。
 
案例演示:
1.在EmployeeMapper接口中加入一個方法
public List<Student> getStuByIdForEach(@Param("ids")List<Integer> ids);

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

<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標籤還能夠用於批量保存數據

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

public void insertStus(@Param("stus")List<Student> student);

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

foreach 中用 collection,collection中是從Mapper接口傳來的參數,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-緩存機制   

MyBatis 包含一個很是強大的查詢緩存特性,它能夠很是方便地配置和定製。緩存能夠極大的提高查詢效率。
只在MyBatis中,在SSM整合文件中沒用,由於SqlSession 定義在 bean.xml中,沒法從新定義SqlSession
 
MyBatis系統中默認定義了兩級緩存。
一級緩存和二級緩存
     一級緩存:(本地緩存):SqlSession級別的緩存,一級緩存是一直開啓的,無法關閉。方法之間不共用!
            與數據庫同一次會話期間查詢到的數據放在本地緩存中。
            之後若是須要獲取相同的數據,直接從緩存中拿,不必再去查詢數據庫;
     二級緩存(全局緩存):
 
        –一、默認狀況下,只有一級緩存(SqlSession級別的緩存,也稱爲本地緩存)開啓。
        –二、二級緩存須要手動開啓和配置,他是基於namespace級別的緩存。
        –三、爲了提升擴展性。MyBatis定義了緩存接口Cache。咱們能夠經過實現Cache接口來自定義二級緩存。
 

一級緩存:

案例:測試一級緩存【默認是開啓的】

將返回一條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種】(沒有使用到當前一級緩存的狀況,效果就是,還須要再向數據庫發出查詢)

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相同,可是查詢條件不同[當前緩存中尚未這個數據]

就是至關於根據不一樣條件再次查找

@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相同,可是兩次查詢之間執行了增刪改操做【此次增刪改可能對當前數據有影響】

由於默認自動刷新了緩存

@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對應一個二級緩存。
【一級緩存的範圍仍是過小了,每次SqlSession一關閉,一級緩存中的數據就消失】
因此從這個角度講:能跨sqlSession的緩存爲二級緩存!
 
工做機制:
1.一個會話,查詢一條數據,這個數據就會被放在當前會話的一級緩存中。
2.若是會話關閉,一級緩存中的數據會被保存到二級緩存中;新的會話查詢信息,就能夠參照二級緩存中。
不一樣namespace查出的數據會放在本身對應的緩存中(map)   
效果:數據會從二級緩存中獲取
          查出的數據都會被默認先放在一級緩存中。
          只有會話提交或者關閉以後,一級緩存中的數據纔會轉移到二級緩存中。
          須要注意的是:在哪一個Mapper.xml文件中開啓了<cache>緩存標籤,哪一個Mapper中就開啓了二級緩存。

 

案例:

1)開啓全局二級緩存配置:

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

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

<cache eviction="FIFO" size="100" readOnly="false"/>

 

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

 

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

4)必須先關閉以前的sqlsession對象

測試:

能夠看到只發送了一次SQL語句,第二次查詢時從二級緩存中拿到的數據,並無發送新的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();
}

須要注意的是:只有一級緩存中關閉的狀況下,二級緩存纔會被使用。

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

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