• 170906-MyBatis续


    ===============================================Dynamic SQL=================================================

    为什么需要动态SQL?有时候需要根据实际传入的参数来动态的拼接SQL语句。

    最常用的就是:where和if标签  

    1.参考官方文档  

    ? if:字符判断  

    ? choose (when, otherwise):分支选择  

    ? trim (where, set):字符串截取;其中where标签封装查询条件,set标签封装修改条件  

    ? foreach  

    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 &amp;&amp; userName !=''">      

    and user_name = #{userName}      

    </if>      

    <if test="email != null and email.trim() != &quot;&quot;">      

    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语句的拼装就会有问题!  

    解决办法:    

    1.给where后面加上1=1,以后的条件都可以使用and xxx了  

    2.mybatis可以使用where标签来将所有的查询条件包括在内。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉!//需要注意:where标签只会去掉第一个多出来的and或者or    

    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 &amp;&amp; userName !=''">      

    user_name = #{userName} and       

    </if>      

    <if test="email != null and email.trim() != &quot;&quot;">      

    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关键字,同时也会消除无关的逗号。      

    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>     

    测试类代码为:   

    @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);   

    }    

    //当然上面的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元素允许指定一个集合,声明集合项和索引变量,并可以指定开闭匹配的字符串以及在迭代之间放置分隔符。    

    案例演示:  

    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标签还可以用于批量保存数据,如下所示:        

    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.测试代码:    

    六、MyBatis-缓存机制     

    MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。  

    MyBatis系统中默认定义了两级缓存。  

    一级缓存和二级缓存。     

    一级缓存:(本地缓存):SqlSession级别的缓存,一级缓存是一致开启的,没法关闭。方法之间不共用!      

    与数据库同一次会话期间查询到的数据放在本地缓存中。    

    以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;   二级缓存(全局缓存):      

    –1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。   

    –2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。   

    –3、为了提高扩展性。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的缓存为二级缓存!               

    工作机制:      

    1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中。      

    2.如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中。      

    3.SqlSession 

    === EmployeeMapper   ===> Employee         

    DepartmentMapper ===> Department         

    不同namespace查出的数据会放在自己对应的缓存中(map)         

    效果:数据会从二级缓存中获取             

    查出的数据都会被默认先放在一级缓存中。          

    只有会话提交或者关闭之后,一级缓存中的数据才会转移到二级缓存中。       

    需要注意的是:在哪个Mapper.xml文件中开启了<cache>缓存标签,哪个Mapper中就开启了二级缓存。            

    使用:    

    1).开启全局二级缓存配置:

    <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;每次查询之前都会清空缓存,缓存是没有被使用!       

  • 相关阅读:
    windows mobile licence
    通过acme.sh生成泛域名证书
    枚举值为何为1、2、4、8、16....
    script 解决 跨域访问
    强大的json字符串对象,转化为object对象
    使用maven jetty调试web程序
    web上实现起点到终点的抛物线轨迹运动——补充
    实现2个有序数组的排序
    maven axis2简单部署webservice
    使用javamail发送邮件
  • 原文地址:https://www.cnblogs.com/12344321hh/p/7485382.html
Copyright © 2020-2023  润新知