MyBatis:动态SQL语句 (1)Xml方式 (2)注解方式 ,MyBatis的缓存,MyBatis的关联查询 ,MyBatis逆向工程

动态SQL语句

动态SQL是什么:

就是相对与固定SQL。就是通过传入的参数不一样,可以组成不同结构的SQL语句. 这种根据参数的条件修改SQL结构的SQL语句,我们称为动态SQL语句.

动态SQL有什么用

 

1.根据条件组装不同结构的SQL语句,可以提高SQL代码的重用性.

 

2.满足某些特定需求,如,条件判断查询

基于XML的实现

 

标签包括

 

<sql>  用于声明公有的SQL语句块.,在操作标签中使用<include>调用 [不建议用]

 

不建议的原因,会导致代码难以维护。

 

<if>  类似java if(){},用于判断

 

<foreach>:类似javaforeach循环,一般用户批量处理的SQL语句

 

<trim> :切割标签,主要用于切割关键字的头和尾的字符.新版的Mybatis使用的几率很少.

 

<set>:使用 set标签就是SQL语言的set关键字,可以在update 的时候set 关键字后面的,逗号可以自动忽略

 

<where>:使用where标签作为SQL语言的where关键字,好处如果where后面的条件都不成立,忽略where关键字.

 

<choose> <when> <otherwise> :  javaswithc case

 

 


<?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="cn.zj.mybatis.mapper.UserMapper">
    <!-- 抽取一个sql片段 <sql id="sql片段的唯一标识"></sql> -->
    <sql id="condition_sql">
        <!-- 动态SQL语句之 where 标签 ,内部具体条件 -->
        <where>
            <!-- 条件 <if test="">标签体内容</test> test :内部是一个布尔类型的结果, 如果为true 标签体中内容生效,如果为false,标签内容无效 
                concat ():串联起来括号内 -->
            <if test="username !=null">username like concat('%',#{username},'%') </if>
            <if test="age !=null">and age = #{age}</if>

        </where>
    </sql>



    <!-- 根据条件查询 -->
    <select id="selectList" parameterType="user" resultType="user">
        <!-- 静态sql语句:语义固定 -->
        <!-- select * from user where name like concat('%',#{name},'%') and age 
            = #{age} -->
        select * from user

        <!-- 引入sql片段 <include refid="condition_sql"/> refid:对应sql片段的id -->
        <include refid="condition_sql" />


    </select>


    <select id="selectTotalByCondition" parameterType="user"
        resultType="long">

        select count(*) from user
        <include refid="condition_sql" />
    </select>

</mapper>

 



package
cn.zj.mybatis.test; import static org.junit.Assert.*; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import cn.zj.mybatis.mapper.UserMapper; import cn.zj.mybatis.pojo.User; import cn.zj.mybatis.util.MyBatisUtil; public class UserMapperTest { @Test public void testName() throws Exception { SqlSession openSession = MyBatisUtil.openSession(); UserMapper mapper = openSession.getMapper(UserMapper.class); User user=new User(); //user.setUsername("e"); user.setAge(20); List<User> selectList = mapper.selectList(user); long selectTotalByCondition = mapper.selectTotalByCondition(user); System.out.println("總數"+selectTotalByCondition); openSession.close(); }}

 


<?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="cn.zj.mybatis.mapper.UserMapper">

    <!-- 修改用户信息 -->
    <update id="updateUserInfo" parameterType="user">
        <!-- 静态sql语句:固定语义 -->
        <!-- update user set name = #{name},password=#{password},age = #{age} where 
            id = #{id} -->
        update user
        <!-- 动态sql语句 set标签 -->
        <set>
        <if test="username !=null">username = #{username},</if>
        <if test="password !=null">password =#{password},</if>
        <if test="age !=null">age =#{age}</if>
        </set>

        where id =#{id}




    </update>


</mapper>

 



@Test
public void testUpdata() throws Exception { SqlSession openSession = MyBatisUtil.openSession(); UserMapper mapper = openSession.getMapper(UserMapper.class); User user = new User(); user.setId(5); user.setPassword("小明"); int updateUserInfo = mapper.updateUserInfo(user); openSession.commit(); openSession.close(); }

 

 

 

package cn.zj.mybatis.test;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import cn.zj.mybatis.mapper.UserMapper;
import cn.zj.mybatis.pojo.User;
import cn.zj.mybatis.util.MyBatisUtil;

public class UserMapperTest {


    @Test
    public void testInsertUserInfo() {
        // 1.创建SqlSession会话对象
        SqlSession session = MyBatisUtil.openSession();
        // 2.创建UserMapper接口的代理对象(Java的动态代理)
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user1 = new User(null, "xireejhef", "wffew", 10);
        User user2 = new User(null, "xijfdhxhef", "wffew", 10);
        User user3 = new User(null, "xijdfhhhef", "wffew", 10);
        List<User> user=Arrays.asList(user1,user2,user3);
        // 3.执行插入方法
        int row = mapper.insertUserInfo(user);
        // 4.提交事务(Mybatis默认DML操作需要手动提交事务)
        System.out.println(row);
        session.commit();
        // 5.关闭session
        session.close();

    }
  <insert id="insertUserInfo" parameterType="user">
     <!-- insert into user (name,password,age)values('张三','zs',18),('李四','ls',20) -->
        <!-- ('张三','zs',18),('李四','ls',20) -->
    insert into user (username,password,age)values
    <foreach collection="users" item="user" separator=",">
    (#{user.username},#{user.password},#{user.age})
    </foreach>
    </insert>

 


    @Test
    public void testDelet() {
        SqlSession openSession = MyBatisUtil.openSession();
        UserMapper userMapper = openSession.getMapper(UserMapper.class);
        Integer[] id= {4,5};
        int user = userMapper.deleteByPrimaryKey(id);

        System.out.println(user);
        openSession.commit();
        openSession.close();

    }
    <!-- 批量删除 -->

    <delete id="deleteByPrimaryKey" parameterType="int">
   
   delete from user where id in
        <foreach collection="ide" item="id" open="(" close=")"
            separator=",">
            #{id}
        </foreach>
    
    
    </delete>

  

 




    @Test
    public void testSelectList() throws Exception {
        SqlSession openSession = MyBatisUtil.openSession();
        UserMapper mapper = openSession.getMapper(UserMapper.class);
        Integer[] id= {1,3};
        List<User> selectList = mapper.selectList(id);
        for (User user : selectList) {
            
        System.out.println(user);
        }
        openSession.close();
    }
}
    <!-- 批量查询 -->
    <select id="selectList" parameterType="int" resultType="user">
        <!-- sql :select * from user where id in(1,2,3,4) 问题: 要动态拼接的sql : (1,2,3,4) 
            答: 使用 foreach 标签 <foreach collection="" item="" open="" close="" separator="">标签体</foreach> 
            collection:需要循环的数组或者集合 item :每次循环出的数据赋值的变量,在标签体中使用 open : 循环开始的 圆括号 ( close 
            :循环结束的圆括号) separator :每次循环的分隔符 逗号 《 -->

        select * from user where id in
        <foreach collection="ids" item="id" open="(" close=")"
            separator=",">
            #{id}
        </foreach>

    </select>
    
    

 

 

基于注解方式实现

动态sql除了支持xml方式以外,还是支持使用纯注解的方式

主要一下四个注解+对应动态sql语句的类文件

  1. @SelectProvider  动态查询SQL语句对应注解
  2. @InsertProvider  动态插入SQL语句对应注解
  3. @UpdateProvider  动态修改SQL语句对应注解
  4. @DeleteProvider  动态删除SQL语句对应注解

 


package cn.zj.mybatis.pojo;

/*
 * 此类是专门编写 User表对应的动态SQL语句的类
 */
public class UserProvide {

    /*
     * 拼接动态sql语句的方法 注意:内部的 参数使用还是使用 OGNL 表达式
     */
    public String selectByCondition(User user) {
        StringBuilder str = new StringBuilder();
        str.append("select * from user WHERE 1 = 1");
        // 拼接动态sql
        if (user.getUsername() != null) {
            str.append(" and username like concat('%',#{username},'%')");
        }
        if (user.getAge() != null) {
            str.append(" and age = #{age}");
        }
        
        return str.toString();
        
    }
    
    public String selectTotalByCondition(User user) {
        StringBuilder str = new StringBuilder();
        str.append("select count(*) from user WHERE 1 = 1");
        // 拼接动态sql
        if (user.getUsername() != null) {
            str.append(" and username like concat('%',#{username},'%')");
        }
        if (user.getAge() != null) {
            str.append(" and age = #{age}");
        }
        
        return str.toString();
    }
    

    
}

 


package
cn.zj.mybatis.test;
    /**
     * 单行查询
     * @param id 主键id
     * @return  查询结果封装的User对象
     */
    /*
     * 查询动态sql语句的注解
     * @SelectProvider(type=cn.zj.mybatis.pojo.UserProvider.class,method="")
     * 属性
     *     type :拼接动态sql语句的类
     *  method :返回具体动态sql语句的方法名称
     *             注意 :方法的参数类型个数,必须和功能方法的参数类型个数一模一样(规则)
     *          建议:返回动态sql语句方法的名称和 当前功能的方法名称一样
     */
    @SelectProvider(type=UserProvide.class,method="selectByCondition")
    List<User> selectTotalByCondition(User user);
    /**
     * 根据提交查询总数
     * @param user
     * @return
     */
    
    @SelectProvider(type=UserProvide.class,method="selectTotalByCondition")
    Long selectCondition(User user);
    

 

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import cn.zj.mybatis.mapper.UserMapper;
import cn.zj.mybatis.pojo.User;
import cn.zj.mybatis.util.MyBatisUtil;

public class UserMapperTest {


    @Test
    public void testSelectList() throws Exception {
    //查詢
        SqlSession openSession = MyBatisUtil.openSession();
        UserMapper mapper = openSession.getMapper(UserMapper.class);
        User users = new User();
        users.setAge(10);
        users.setUsername("1");
        mapper.selectTotalByCondition(users);
        mapper.selectCondition(users);
        openSession.close();
    }
}

 


package cn.zj.mybatis.pojo;

/*
 * 此类是专门编写 User表对应的动态SQL语句的类
 */
public class UserProvide {

    
    public String updateUserInfoa(User user) {
    StringBuilder str = new    StringBuilder();
    str.append("update user set ");
    if (user.getAge() !=null) {
        str.append("age =#{age},");
    }
    if (user.getUsername() !=null) {
        str.append("username =#{username},");
    }
    if (user.getPassword() !=null) {
        str.append("password =#{password},");
    }
    // 删除最后一个逗号
    str.deleteCharAt(str.length()-1);
    str.append(" where id =#{id}");
    return str.toString(); 
    }
    
    
    
    
    
    
}

 

 
@UpdateProvider(type=UserProvide.class,method="updateUserInfoa")
    int updateUserInfo(User user);

 


@Test
public void testupdate() { //修改 SqlSession openSession = MyBatisUtil.openSession(); UserMapper userMapper = openSession.getMapper(UserMapper.class); User user = new User(); user.setId(6); user.setUsername("mmmmmmmm"); user.setAge(100); user.setPassword("abcabc"); int user1 = userMapper.updateUserInfo(user); System.out.println(user1); openSession.commit(); openSession.close(); }
package cn.zj.mybatis.pojo;

import org.apache.ibatis.annotations.Param;

/*
 * 此类是专门编写 User表对应的动态SQL语句的类
 */
public class UserProvide {

    public String deleteByPrimaryKeya(@Param("ide")Integer[] ide) {
    StringBuilder sb = new    StringBuilder();
    sb.append("delete from user where id in (");
    for (int i = 0; i < ide.length; i++) {
        sb.append("#{ide["+i+"]},");
    }
    sb.deleteCharAt(sb.length()-1);
    sb.append(")");
    
        return sb.toString();
        
    }
    
    
    
    
    
    
}

 

@DeleteProvider(type=UserProvide.class,method="deleteByPrimaryKeya")
    int deleteByPrimaryKey(@Param("ide")Integer[] ide);
    

 


@Test
public void testDelet() { //删除 SqlSession openSession = MyBatisUtil.openSession(); UserMapper userMapper = openSession.getMapper(UserMapper.class); Integer[] id= {6,7}; int user = userMapper.deleteByPrimaryKey(id); System.out.println(user); openSession.commit(); openSession.close(); }
  
package cn.zj.mybatis.pojo;

import java.util.List;

import org.apache.ibatis.annotations.Param;

/*
 * 此类是专门编写 User表对应的动态SQL语句的类
 */
public class UserProvide {

    public String insertUserInfo(@Param("users")List<User> users) {
    StringBuilder sb = new    StringBuilder();
    sb.append("insert into user(Username,password,age) values ");
    for (int i = 0; i <users.size(); i++) {
        sb.append("(#{users[" + i + "].username},#{users[" + i + "].password},#{users[" + i + "].age}),");
    }
    sb.deleteCharAt(sb.length()-1);
    
    
        return sb.toString();
        
    }
    
    
    
    
    
    
}

 



      
    /**
     * 批量插入
     * @param users
     * @return
     */
    @InsertProvider(type=UserProvide.class,method="insertUserInfo")
    int insertUserInfo(@Param("users")List<User> users);
    

 


@Test
public void testInsertUserInfo() { // 1.创建SqlSession会话对象 SqlSession session = MyBatisUtil.openSession(); // 2.创建UserMapper接口的代理对象(Java的动态代理) UserMapper mapper = session.getMapper(UserMapper.class); User user1 = new User(null, "xireejhef", "wffew", 10); User user2 = new User(null, "xijfdhxhef", "wffew", 10); User user3 = new User(null, "xijdfhhhef", "wffew", 10); List<User> user=Arrays.asList(user1,user2,user3); // 3.执行插入方法 int row = mapper.insertUserInfo(user); // 4.提交事务(Mybatis默认DML操作需要手动提交事务) System.out.println(row); session.commit(); // 5.关闭session session.close();

 

 缓存

Mybatis里面,所谓的缓存就是将已经查询过的记录放在内存的缓冲区或文件上,这样如果再次查询,可以通过配置的策略,命中已经查询过的记录.从而提高查询的效率.

缓存作用

提高查询的效率.

 一级缓存

Mybatis的缓存分为一级缓存\ 二级缓存

 

一级缓存:所谓的一级缓存就是会话(SqlSesion对象)级别的缓存,就是同一个会话,如果已经查询过的数据会保存一份在内存中,如果会话没有关闭,再次调用同样的方法查询,不会再查询数据库,而是直接从缓存中取出之前查询的数据.

 

一级缓存默认是打开的,而且是关闭不了的.

 

如何清空一级缓存.

1.关闭会话.close()

2.进行了操作(增删改),提交了commit();

3.手工清除缓存clearCache()

 

二级缓存

 

一级缓存是SqlSession对象级别,在每一次会话中有效

 

 

 

二级缓存是 SqlSessionFactory级别,在整个应用都有效,可以在多个会话有效

 

MyBatis本身并没有实现二级缓存

 

 

 

二级缓存需要第三方缓存提供商的支持

 

Ehcache -第三方缓存(Hibernate框架默认就是支持)

 

 

 

学习地址

 

http://www.mybatis.org/ehcache-cache/

 

下载ehcache

 

https://github.com/mybatis/ehcache-cache/releases

 

配置开启二级缓存

MyBatis开启二级缓存新版本已经默认支持开启二级缓存.可以不改

<settings>

<!-- 开启二级缓存 -->

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

</settings>

1.导包

2.配置文件

 

缓存的xml

<ehcache>
    <!-- 缓存的磁盘位置 -->
    <diskStore path="D:/mybatis_cache"/>
    <!-- 默认的缓存策略: 如果开发者在某一个需要缓存的文件配置了自定义缓存,就不使用默认的,如果没有配置,就使用默认缓存策略-->
    
    <!-- 
        maxElementsInMemory="10000" 内存最大缓存的对象个数
        eternal="false" 是否是永久有效
        timeToIdleSeconds="120"  最大缓存时长
        timeToLiveSeconds="120"  最大有效时长
        overflowToDisk="true"  超出内存个数以后是否缓存到磁盘
    
     -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
      />
</ehcache>

缓存日志的properties文件

 

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.cn.zj.mybatis.mapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

 

 

 

 

在映射文件中配置<cache>以及配置对应的缓存策略

 

<mapper namespace="cn.zj.mybatis.dao.UserMapper">
    
    <!-- 当前表的映射开启支持二级缓存,并设置相关的缓存提供商,以及缓存的相关配置 -->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache" >
       <!--最大的空闲时间  -->
       <property name="timeToIdleSeconds" value="10000"/>
       <!-- 最大的在线时间 -->
       <property name="timeToLiveSeconds" value="20000"/>
       <!-- 内存的大小 b字节 m1 =1024k 1k=1024b -->
       <property name="maxEntriesLocalHeap" value="2000000"/>
       <!-- 文件的大小 b字节-->
       <property name="maxEntriesLocalDisk" value="20000000"/>
       <!-- 算法 LRU:最少使用优先, "LFU" or "FIFO:先进先出 -->
       <property name="memoryStoreEvictionPolicy" value="LRU"/>
    </cache> 
    <select id="selectAll" resultType="User">
        select * from user
    </select>
</mapper>

 在对象类中序列化

因为二级缓存可以缓存到文件(将对象序列化到本地),涉及到对象序列化,那么对应的javaBean对象就必须实现 

 

 

缓存的命中率

命中率= 从缓存中获取数据的次数/ 查询的总次数

: 两次查询 从缓中获取一次

0.5 =  1/2;

0.666666 = 2/3;

命中率越高缓存效果越好

 

 

 MyBatis的对象关系映射(难点重点)

 

在实际开发中,一个业务可能涉及到多个数据表的查询,那么多表查询就涉及连接查询(等值连接), 等值连接 表与表之间有一个外键关键

 

但是程序中最终获取的表封装的对象, 对象与对象之间是没有外键关系的,对象和对象之间只有依赖关系

 

对象之间关系主要是四种

 

一对一 关系
一个人对应身份证id,一个QQ号对应一个QQ空间

一对多 关系

    一个部门对应多个员工

多对一 关系

   多个员工对应一个部门

多对多 关系

   多个学生对应多个老师,多个学生对应多个课程

什么关系应该从哪个对象作为中心点来看

一对多, one方作为中心点

 

MyBatis框架支持多表查询封装对象之间关系

 

<collection> 一对多查询

<association>多对一和一对一查询

 

 

多对1,以age为外键

 

 

 

 

 

 

 

 

 

一对多

 

 

<?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="cn.zj.mybatis.mapper.UserMapper">
    <select id="testSelect" resultMap="use">
        select * from user2 where age =#{age}
    </select>
    <!-- 手动映射 -->
    <resultMap type="cn.zj.mybatis.pojo.User2" id="use">
    
    
    <result column="name" property="name"/>
    
    <!-- 
              问题: private User user; 部门对象如何映射?
               
               解决方案:使用 联合查询标签
                <collection property="" column="" select=""/>
                property :需要映射的pojo对象的属性此时就是  dept
                column :已知对应dept对象的外键列
                select :需要去查询的功能id
           -->
           <collection property="user" column="age" select="findByDeptId"/> 
    
    </resultMap>

<!-- 联合查询的功能 -->
      
      <!-- 根据部门的id查询出对应的部门对象-->
      <select id="findByDeptId" parameterType="Integer" resultType="cn.zj.mybatis.pojo.User">
      
      select * from user where age = #{age}
      </select>
</mapper>

 

 

 

结果:

 

 

等值连接查询(1对多,其他和上面一样)

<?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="cn.zj.mybatis.mapper.UserMapper">
    <select id="testSelect" resultMap="use">
        select u.id u_id ,u.password u_password,u.username u_username,s.age s_age,s.name s_name
         from user u,user2 s where u.age =s.age and s.age=#{age}
    </select>
    <!-- 手动映射 -->
    <resultMap type="cn.zj.mybatis.pojo.User2" id="use">
    
    <id column="s_age" property="age"/>
    <result column="s_name" property="name"/>
    <!-- 
              问题: private List<User> user; 员工集合如何映射?
              解决方案:使用 <collection>集合标签,在内部映射多方(员工)的信息
              <collection property="" column="" select=""/>    
              property :需要映射的属性 此时就是 user
              ofType:需要映射集合user 的泛型
           -->
           <collection property="user" ofType="cn.zj.mybatis.pojo.User"> 
    <id column="u_id" property="id"/>
    <result column="u_username" property="username"/>
    <result column="u_password" property="password"/>
    <result column="u_age" property="age"/>
    </collection>
    </resultMap>


</mapper>

 

MyBatis的逆向工程

MyBatis的逆向工程能自动帮开发者生成数据库表对应的 pojo实体文件,自动生成映射文件

自定生成表的各种(CRUD)sql语句, 但是只能做单表操作,联合查询还得开发者自己动

 

使用逆向工程得先在Eclipse安装逆向工程的插件

1.安装插件

2.新建一个普通java项目,导入mybatis.jar包和数据库驱动包

3.

posted @ 2019-07-03 09:42  茫茫林海  阅读(395)  评论(0编辑  收藏  举报