Mybatis-06 动态Sql

Mybatis-06 动态Sql

Mybatis系列文章已经完成上传:
一、什么是Mybatis
二、CRUD
三、配置解析
四、日志、分页
五、注解开发
六、动态Sql

多对一处理

多个学生,对应一个老师

对于学生这边而言,关联多个学生,关联一个老师 【多对一】

对于老师而言,集合,一个老师又很多学生 【一对多】

1.创建数据库

1

2

2.创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class teacher {

    private int id;
    private String name;

}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class student {
    
    private int id;
    private String name;
    private teacher teacher;

}

3.接口类

public interface StudentMapper {

    public List<student> getStudent();

}

4.Mapper.xml文件

思路:

  1. 查询出所有学生
  2. 根据tid查询其对应老师

复杂的对象就用associationcollection

对象:association 集合:collection

4.1 按照查询嵌套处理

<mapper namespace="com.Dao.StudentMapper">
    <resultMap id="stutea" type="pojo.student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" column="tid" javaType="pojo.teacher" select="getTeacher"/>
    </resultMap>
     <select id="getStudent" resultMap="stutea">
         select * from mybatistest.stu
     </select>
    <select id="getTeacher" resultType="pojo.teacher">
         select * from mybatistest.teacher where id = #{id}
    </select>
</mapper>

4.2 按照结果嵌套处理

<mapper namespace="com.Dao.StudentMapper">
    <select id="getStudent" resultMap="studentTeacher2">
            select s.id,s.name,t.name
            from mybatistest.stu s,mybatistest.teacher t
            where s.tid=t.id
    </select>
    <resultMap id="studentTeacher2" type="pojo.student">
            <result property="id" column="id"/>
            <result property="name" column="name"/>
            <association property="teacher" javaType="pojo.teacher">
                <result property="name" column="name"/>
            </association>
    </resultMap>
</mapper>    

5.测试

 @Test
    public void getStudent(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<student> student = mapper.getStudent();
        for (pojo.student student1 : student) {
            System.out.println(student1);
        }
        sqlSession.close();
    }

3

4

一对多处理

数据库不变

1.创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class teacher {

    private int id;
    private String name;
    private List<student> students;

}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class student {

    private int id;
    private String name;
   // private teacher teacher;
    private int tid;

}

2.接口类

public interface TeacherMapper {

     public teacher getTeacher(@Param("tid") int id);

}

3.Mapper.xml文件

3.1 按照查询嵌套处理

<mapper namespace="com.Dao.TeacherMapper">
    
    <select id="getTeacher" resultMap="geTeacher" >
        select * from mybatistest.teacher where id = #{tid}
    </select>
    <resultMap id="geTeacher" type="pojo.teacher">
        <collection property="students" javaType="ArrayList" ofType="pojo.student" select="getStudent" column="id"></collection>
    </resultMap>
    <select id="getStudent" resultType="pojo.student">
        select * from mybatistest.stu where tid = #{tid}
    </select>
    
</mapper>

3.2 按照结果嵌套处理

<mapper namespace="com.Dao.TeacherMapper">

    <select id="getTeacher" resultMap="teacherStudent">
        select t.id tid,t.name tname,s.id sid,s.name sname
        from mybatistest.stu s,mybatistest.teacher t
        where s.tid=t.id and t.id=#{tid}
    </select>
    <resultMap id="teacherStudent" type="pojo.teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="pojo.student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

</mapper>

4.测试

 @Test
public void getTeacher(){
      SqlSession sqlSession = mybatis_util.getSqlSession1();
      TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
      teacher teacher = mapper.getTeacher(1);
      System.out.println(teacher);
      sqlSession.close();
}

5

6

ofType & javaType

  1. javaType用来指定实体类中属性
  2. ofTyoe用来指定映射到List或者集合中pojo类型,泛型中的约束类型

注意点:注意一对多和多对一中,属性名和字段的问题

动态sql

动态SQL就是指根据不同的条件生成不同的SQL语句

  • If
  • choose (when,otherwise)
  • trim (where,set)
  • foreach

1.基础准备

1.1 创建数据库

CREATE TABLE `blog`(
`id` INT(10) NOT NULL COMMENT '博客id',`title` VARCHAR(20) NOT NULL COMMENT '博客标题',`author` VARCHAR(10) NOT NULL COMMENT '作者',`create_time` DATETIME NOT NULL COMMENT '创建时间',`views` INT(20) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB CHARSET=utf8;

7

1.2 创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

1.3 创建接口类

public interface BlogMapper {

    public int addBlog(Blog blog);

}

1.4 创建Mapper.xml文件

<mapper namespace="com.Dao.BlogMapper">
    <insert id="addBlog" parameterType="pojo.Blog">
        insert into mybatistest.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>
</mapper>

1.5 测试代码

  @Test
    public void Test(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog(1,"title","张",new Date(),11);
        int i = mapper.addBlog(blog);
        System.out.println(i);
    }

2.IF

接口

public interface BlogMapper {

   public List<Blog> queryBlogIF(Map map);

}

映射文件

<mapper namespace="com.Dao.BlogMapper">
    
    <select id="queryBlogIF" parameterType="map" resultType="pojo.Blog">
        select * from mybatistest.blog where 1=1
        <if test="views != null">
            and views > #{views}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
        <if test="title != null">
            and title like #{title}
        </if>
    </select>

</mapper>

测试

   @Test
    public void queryBlogIF(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String,Object> map = new HashMap<String,Object>();
        map.put("views",10);
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }

8

注意:

  • 未绑定mapper

在配置文件中绑定:

<mappers>
   <mapper class="com.Dao.BlogMapper"/>
</mappers>
  • createTime数据为null

这是因为在实体类中,数据库中定义时间属性为:create_time,有_

可以开启驼峰命名法映射,在配置文件中加入:

  <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
  </settings>

在数据库字段命名规范中常常用下划线 "_" 对单词进行连接,如:"create_time",而开发中实体属性通常会采用驼峰命名法命名为 createTime

3.choose (when,otherwise)

接口

public interface BlogMapper {

   public List<Blog> queryBlogChoose(Map map);

}

映射文件

<select id="queryBlogChoose" parameterType="map" resultType="pojo.Blog">
      select * from mybatistest.blog
      <where>
         <choose>
              <when test="title != null">
                 and title like #{title}
             </when>
             <when test="author != null">
                 and author = #{author}
             </when>
             <otherwise>
                 and views > #{views}
             </otherwise>
        </choose>
     </where>
</select>

测试

    @Test
    public void queryBlogChoose(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String,Object>();
        map.put("title","%啦%");
        List<Blog> blogs = mapper.queryBlogChoose(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }

9

4.trim (where,set)

where元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 ANDORwhere 元素也会将它们去除。

<where>
    <if test="views != null">
        views > #{views}
    </if>
    <if test="author != null">
        and author=#{author}
    </if>
    <if test="title != null">
        and title like #{title}
    </if>
</where>

set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。

接口

 public int updateBlogSet(Map map);

映射文件

<update id="updateBlogSet" parameterType="map">
    update mybatistest.blog
    <set>
        <if test="title != null">title=#{title},</if>
        <if test="author != null">author=#{author},</if>
        <if test="views != null">views=#{views},</if>
    </set>
    where id=#{id}
</update>

测试

    @Test
    public void updateBlogSet(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String,Object>();
        map.put("id",1);
        map.put("title","t-test");
        map.put("author","a-test");
        map.put("views",100);
        int i = mapper.updateBlogSet(map);
        System.out.println(i);
        HashMap map1 = new HashMap();
        List<Blog> blogs = mapper.queryBlogIF(map1);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }

10

5.Foreach

接口

public List<Blog> queryBlogForeach(Map map);

映射文件

<select id="queryBlogForeach" parameterType="map">
    select * from mybatistest.blog
    <where>
        /*此处的collection是一个list,所以map需要传入一个list来进行遍历*/
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id=#{id}
        </foreach>
        <if test="views != null">
            and views > #{views}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </where>
</select>

测试

    @Test
    public void queryBlogForeach(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String,Object>();
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(2);
        ids.add(3);
        map.put("ids",ids);
        List<Blog> blogs = mapper.queryBlogForeach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }

11

6.Sql片段

我们可以将一些公共的部分用<sql>抽取出来,方便复用!

<sql id="id-test">
    <choose>
        <when test="title != null">
            and title like #{title}
        </when>
        <when test="author != null">
            and author = #{author}
        </when>
        <otherwise>
            and views > #{views}
        </otherwise>
    </choose>
</sql>
<select id="queryBlogChoose" parameterType="map" resultType="pojo.Blog">
   select * from mybatistest.blog
   <where>
       <include refid="id-test"></include>
   </where>
</select>

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了

我们可以先在Mysql中写出完整的SQL,在对应的去修改称为我们的动态SQL

缓存

1.简介

查询:连接数据库,耗资源!

一次查询的结果,给他暂存在一个可以直接取到的地方——内存:缓存

那么我们再次查询的时候就可以不用走数据库了

  1. 缓存【Cache】?
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存中,用户查询的时候就不用从磁盘上查询了,而从缓存中查询,提高查询效率
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据

2.Mybatis缓存

Mybatis系统中默认顶一个两级缓存:一级缓存和二级缓存

  • 默认情况下,只有一级缓存开启。这是sqlSession级别的,随着Session开启而开启,关闭而关闭,也称其为本地缓存
  • 二级缓存是namespace级别的,需要手动开启和配置
  • Mybatis有一个配置缓存的接口Cache,可以定义二级缓存

注意事项:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU,Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

3.一级缓存

一级缓存也叫本地缓存:

  • 在域数据库交互的同一个会话中,会将查过的数据放在缓存中
  • 以后再查询相同的数据时,直接从缓存中取数据

测试

  1. 开启日志
  2. 测试两次查询同一条数据
    @Test
    public void cache(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        user user = mapper.getUserById(1);
        System.out.println(user);
        System.out.println("===============================");
        user user1 = mapper.getUserById(1);
        System.out.println(user1);
        System.out.println(user==user1);
        sqlSession.close();
    }

12

从图中可以看出,数据在一级缓存,只查询一次,这两者相同,为true

手动清理缓存

    @Test
    public void cache(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        user user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.clearCache();    //手动清理缓存
        System.out.println("===============================");
        user user1 = mapper.getUserById(1);
        System.out.println(user1);
        System.out.println(user==user1);
        sqlSession.close();
    }

13

从图中可以看出,数据在一级缓存,手动清理缓存后,查询了两次,这两者不同,为false

4.二级缓存

二级缓存是基于namespace的缓存,它的作用域比一级大

  • 我们希望当会话关闭的时候,存储在一级缓存的数据可以进入二级缓存
  • 用户进行第二次会话的时候,就可以直接从二级缓存拿数据

4.1 开启缓存

在配置文件开启二级缓存

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

在对应的mapper.xml中选择开启二级缓存

<cache/>

也可以自定义cache

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

4.2测试

      @Test
      public void SecondCache(){
          SqlSession sqlSession = mybatis_util.getSqlSession();
          UserDao mapper = sqlSession.getMapper(UserDao.class);
          user user = mapper.getUserByID(1);
          System.out.println(user);
          sqlSession.close();
          System.out.println("===============================");
          SqlSession sqlSession1 = mybatis_util.getSqlSession();
          UserDao mapper1 = sqlSession1.getMapper(UserDao.class);
          user user1 = mapper1.getUserByID(1);
          System.out.println(user1);
          System.out.println(user==user1);
          sqlSession.close();
      }

14

从图中可以看出,开启二级缓存后,sqlSession关闭时,数据存入二级缓存,直接在二级缓存调出数据,只用查询了一次 ,这两者不同,为false

注意:可能会出现的错误:Error serializing object. Cause:java.io.NotSerializableException: pojo.user,这个错误只需要在实体类继承Serializable,即:class user implements Serializable

5.缓存原理

15

6.自定义缓存-encache

Ehcache是一种广泛使用的开源Java分布式缓存。EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

6.1 导入依赖

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>

6.2 导入配置文件

创建ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>

6.3 开启二级缓存

 <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

其实没什么大的区别,想用可以用

个人博客为:
MoYu's Github Blog
MoYu's Gitee Blog

相关文章

1.pom.xml引入依赖 &lt;dependency&gt; &lt;gro...
&lt;?xml version=&quot;1.0&quot; encoding=&a...
准备工作 ① 创建数据库&amp;数据表 ## 创建数据库 CREA...
MyBatis逆向工程是指根据数据库表结构自动生成对应的实体类、...
MyBatis获取参数值的两种方式:${}和#{} ${}的本质就是字符串...
resultMap作用是处理数据表中字段与java实体类中属性的映射关...