Mybatis学习笔记

1 Mybatis应用五部曲

1.创建实体类(ORM,与数据库数据对应)

2.创建Dao接口

3.用mapper,一个xml文件实现Dao接口

4.mybatis-config.xml里注册mapper

5.测试

 

 

2 CRUD

2.1 基本的增删改查

注意:

1.增删改需要提交事务

2.参数和返回值为基本类型可以不写

====Dao
int deleteUser(int id);
====mapper
<delete id="deleteUser">

2.2 模糊查询

方式一:在Java语句里添加%%

List<User> users = userdao.getUserByNameLike("%xiao%");

方式二:在sql里添加%%

select * from mybatis.user where name like "%"#{any}"%"

  

 

3 参数用map取代实体类及一切其他类

3.1 例子

比如

public interface UserDao {
    User getUserById(int id);
    int addUser(User user);
    int updateUser(User user);
    int deleteUser(int id);
}

参数可全部替换为map

public interface UserDao {
    List<User> getUserList(Map<String,Object> map);
    User getUserById(Map<String,Object> map);
    int addUser(Map<String,Object> map);
    int updateUser(Map<String,Object> map);
    int deleteUser(Map<String,Object> map);
}

通过传递一个map就可以取代一切其他类型(实体类,基础类,,,)

HashMap<String, Object> map = new HashMap<>();
map.put("any1", 5);
map.put("any2", "test5");
map.put("any3", "12345");
​
userdao.addUser2(map);

3.2 打破了什么限制?

如果用实体类,则参数名被限制得死死的,不能有一丝疏忽。如果用map,参数名可自行设置。

3.3 参数一般用map或者注解

 

 

4 核心配置文件篇

 

4.1 环境配置(environments)

 

事务管理器

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

 

数据源

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

 

 

 

4.2 映射器(mappers)

注意

1.第二中不建议使用。

2.第三第四种,

(1)接口名与Mybatis的映射文件名一定要一模一样。即UserMapper(接口)和UserMapper.xml。

(2)必须把Mapper接口和XML配置文件放在同一包下才能使用。

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) 一般不用-->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
​
​
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers> 

 

4.3 属性(properties)

就是一个键值对,在任何可以使用<properties resource="">标签的地方都可以引用。

补充:xml中,标签可规定顺序。mybatis核心配置文件就是。

 

4.4 类型别名(typeAliases)

为Java类型设置一个较短(较好记)的名字

1 为类设置一个别名

语法

<typeAliases>
  <typeAlias type="domain.blog.Author" alias="Author"/>
    ...
</typeAliases>

2 指定一个包名,批量为类设置别名

扫描实体类的包,批量为实体类设置别名。它的默认别名就是这个类的类名(首字母小写)。

语法

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

3 补充(注解的方式)

若有注解,则最终使用注解名。

@Alias("author")
public class Author {
    ...
}

”加_是基本类型,不加_是包装类型?“ ???没听懂

 

 

 

 

 

4.5 设置(settings)

完整settings

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

我们只需掌握

设置名描述有效值默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true|false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true|false false
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J SLF4J ,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING 未设置

spring以后也不用了。

 

4.6 其他配置

插件(plugins)

  • mybatis-generator-core

  • mybatis-plus

  • 通用mapper

 

 

 

5 作用域和生命周期

作用域和生命周期至关重要,错误的使用会导致非常严重并发问题

SqlSessionFactoryBuilder:

一旦创建了SqlSessionFactory,就不再需要了。

SqlSessionFactory:

类似数据库连接池。一旦被创建就应该在应用运行周期一直存在。

SqlSession:

类似连接到连接池的一个请求,连接完需关闭。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

 

 

 

6 映射文件(mapper设置)

6.1 九个元素

1 元素(CRUD)的属性

 selectinsertupdatedelete
parameterType
databaseId
flushCache
lang
parameterMap
statementType
timeout
keyColumn    
keyProperty    
useGeneratedKeys    
resultMap      
resultType      
fetchSize      
resultOrdered      
resultSets      
resultSetType      
useCache      

select独有的resultMap,resultType,fetchSize,resultOrdered,resultSets,resultSetType,useCache。

insert和update独有的keyColumn,keyProperty,userGeneratedKeys。

2 元素(resultMap)的属性

打算细细分析一波!另一篇存档。Mybatis-resultMap.md

resultMap 是mybatis中最重要最强大的元素!

  • constructor - 用于在实例化类时,注入结果到构造方法中

    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能

    • arg - 将被注入到构造方法的一个普通结果

  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

  • result – 注入到字段或 JavaBean 属性的普通结果

  • association – 一个复杂类型的关联;许多结果将包装成这种类型

    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用

  • collection – 一个复杂类型的集合

    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用

  • discriminator– 使用结果值来决定使用哪个resultMap

    • case– 基于某些值的结果映射

      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射 

案例一

实体类
id user_id
name user_name
password user_password

solv1.sql语句起别名

select user_id as id,user_name as name,user_password as password from ...

solv2.resultMap(结果集映射)

    <resultMap id="UserMap" type="pojo.User">
        <!--column列->数据库中的字段,property属性->实体类中的属性-->
        <result column="user_id" property="id"/>
        <result column="user_name" property="name"/>
        <result column="user_password" property="password"/>
    </resultMap>
    <!--id方法名-->
    <!---->
    <select id="getUserList" resultMap="UserMap">
        select * from mybatis.user
    </select>

注意:属性和字段相同的地方不需要映射。

 

 

7 日志

7.1 日志工厂

用处:数据库操作出现异常需要排错

曾今:sout,debug。现在:日志工厂。

在4(核心配置文件篇).5里的logImpl提到过。需要在配置文件里配置

掌握:LOG4J(需要导包+配置),STDOUT_LOGGING(设置就好)

其他:了解

<!--日志配置:不允许有空格-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

 

 

8 分页

8.1 limit实现分页

#接口
List<User> getUsersBylimit(Map<String,Integer> map);
#Mapper
<select id="getUsersBylimit" parameterType="map" resultMap="UserMap">
    select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
#test
    @Test
    public void test11(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Integer> hashMap = new HashMap<>();
        hashMap.put("startIndex", 0);
        hashMap.put("pageSize", 3);

        List<User> users = mapper.getUsersBylimit(hashMap);
        for (User user : users) {
            System.out.println(user);
        }

        sqlSession.close();
    }

8.2 RowBounds实现分页

#接口(即查询全部)
List<User> getUserList();
#mapper(即查询全部)
<select id="getUserList" resultMap="UserMap">
    select * from mybatis.user
</select>
#test
    @Test
    public void test12(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //通过java代码层面实现分页
        RowBounds bounds = new RowBounds(0, 3);
        List<User> users = sqlSession.selectList("dao.UserMapper.getUserList",null,bounds);
//注意,路径是dao接口不是mapper实现
        /*查看全部则是*/
        /*List<User> userss = sqlSession.selectList("dao.UserMapper.getUserList");*/
        for (User user : users) {
            System.out.println(user);
        }

        sqlSession.close();
    }

3.分页插件

有很多,知道有这东西即可。

https://pagehelper.github.io/

 

 

9 注解开发

临时补充1

面向接口编程:解耦,可拓展,提高复用,利于分层开发。

接口与实现分离。接口是骨架,实现是肉。骨架确定,肉怎么长没关系。

接口有两类:

1.抽象体(abstract class),对一个个体的抽象。

2.抽象面(interface),对一个个体某一方面的抽象。

临时补充2

自动提交事务commit

public static SqlSession getSqlSession(){
//return sqlSessionFactory.openSession();
return sqlSessionFactory.openSession(true);
}

 

9.1 关于注解开发

注解在mybatis文档所言不多。

使用方法是:在接口上面增加一条注解就可以。

注意:

1.XML和注解不能同时作用。

2.使用注解时,在配置文件中绑定接口时(可以使用映射器第三第四种方法而不必将dao和mapper放在同一路径下【因为此时压根没用上mapper,或者说mapper用注解的方式在dao路径下实现了】)。很鸡肋,当我没说!

本质:

反射机制实现。

底层:

动态代理。

 

9.2 注解CRUD

    @Select("select user_id as id,user_name as name,user_password as password from user where user_id=#{id}")
    User getUserById(@Param("id") int any1);

    @Insert("insert into mybatis.user(user_id,user_name,user_password) values(#{id},#{name},#{password})")
    int addUser(User user);
    int addUser2(Map<String,Object> map);

    @Update("update mybatis.user set user_name=#{name},user_password=#{password} where user_id=#{id};")
    int updateUser(User user);

    @Delete("delete from mybatis.user where user_id = #{uid}")
    int deleteUser(@Param("uid") int any);

关于Param注解

  • 多个基本类型的参数和String类型,必须加上。

  • 引用类型,不需要加。

  • 一个基本类型,不需要加

#{}和${}的区别

#{}会自动加上"",很大程度防止sql注入。

${}则不会。

 

 

10 mybatis的详细执行流程

Resources获取全局配置文件

实例化SqlsessionFactoryBuilder构造器

  解析配置流文件XMLConfigBuilder

  Configuration所有的配置信息

SqlsessionFactory实例化 ↓

 

11 lombok

用前须知:一旦启用lombok插件,那么团队中其他成员也必须安装此插件,否则会编译报错。

使用步骤:

1.在IDEA中安装Lombok插件

setting->plugins->Browse repositories-> emmm等了很久没找到,换硬盘安装。

安装教程参考https://www.cnblogs.com/han-1034683568/p/9134980.html

因为存在版本 兼容问题(笔者IDEA2018.2.5破解版),所以选择

setting->plugins->install plugin from disk->选中保存文件,重启IDEA。

2.导包

->maven仓库

3.使用

 

 

 

@Data:无参构造,get,set,toString,hashCode,equals,canEqual

@AllArgsConstructor:有参构造

以上两个就够了!

package pojo;

import lombok.*;

@Data
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}

 

 

 

12 复杂的查询环境

对学生而言:多个学生关联一个老师,多对一

一个学生实体里包含了一个老师对象。

对老师而言:一个老师集合多个学生,一对多

一个老师实体里包含学生对象的集合

 

12.1 多对一

1 环境搭建

create table `teacher`(
  `id` int(10) not null primary key ,
  `name` varchar(30) default null
) ENGINE=INNODB default charset=utf8;

insert into teacher(`id`,`name`) values (1,'Teacheryang');

create table `student`(
  `id` int(10) not null primary key ,
  `name` varchar(30) default null,
  `tid` int(10) default null,
  key `fktid` (`tid`),
  constraint `fktid` foreign key (`tid`) references `teacher` (`id`)
) ENGINE=INNODB default charset=utf8;

insert into `student`(`id`,`name`,`tid`) values (1,'stu1','1');
insert into `student`(`id`,`name`,`tid`) values (2,'stu2','1');
insert into `student`(`id`,`name`,`tid`) values (3,'stu3','1');
insert into `student`(`id`,`name`,`tid`) values (4,'stu4','1');
insert into `student`(`id`,`name`,`tid`) values (5,'stu5','1');

lombox+注解开发都用上,简化环境搭建。

老师实体类

@Data
//无需有参构造
public class Teacher {
    private int id;
    private String name;
}

学生实体类

@Data
public class Student {
    private int id;
    private String name;
    private Teacher teacher;(这就是上面说的:一个学生实体里包含了一个老师对象)
}

 

查询所有的学生信息,以及对应辅导员的信息。

👇

2 按照查询嵌套处理(对应sql:子查询。较难理解)

#接口

List<Student> getStudent();

#mapper

<!--
思路:
     1.查询所有的学生信息
     2.根据查询出来的学生的tid,寻找对应的老师。
-->
<select id="getStudent" resultMap="Student_Associate_Teacher">
    select * from mybatis.student
</select>

<select id="getTeacher" resultType="pojo.Teacher">
    select * from mybatis.teacher where id=#{id}
</select>

<resultMap id="Student_Associate_Teacher" type="pojo.Student">
    <!--实体和相同可以不写-->
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="teacher" column="tid" javaType="pojo.Teacher" select="getTeacher"/>
</resultMap>

小小分析不难理解:

1.第一个SELECT语句拿到所有的学生信息,但是从数据表里只能拿到教师的tid。需要由tid得到教师全部信息。

2.构造第二个SELECT语句,由教师id得到教师全部信息。

3.返回类型必须是结果集映射resultMap类型,构造此类型。

3 按照结果嵌套处理(对应sql:联表查询。较简单)

#接口

List<Student> getStudent2();

#mapper

    <select id="getStudent2" resultMap="Student_Associate_Teacher2">
        select s.id sid,s.name sname ,t.id tid,t.name tname from student s,teacher t where s.tid=t.id;
    </select>
    <resultMap id="Student_Associate_Teacher2" type="pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="pojo.Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

小小分析不难理解:

 

 

 

12.2 一对多

1 环境搭建

数据库环境和刚才一样

项目组织结构和刚才一样

=========开始==========

学生实体类

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}

老师实体类

@Data
public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}

2 按照查询嵌套处理

#接口

Teacher getTeachers2(int id);

#mapper

    <select id="getTeachers2" resultMap="Teacher_collection_students2">
        select * from mybatis.teacher where id = #{tid}
    </select>

    <select id="getStudent" resultType="pojo.Student">
        select * from mybatis.student where tid=#{any_tid}
    </select>

    <resultMap id="Teacher_collection_students2" type="pojo.Teacher">
        <!--很奇怪,这里id如果不进行映射赋值,得到的教师id为0(本该为1)。-->
        <result property="id" column="id"/>
        //javaType和ofType为返回类型,students为返回变量名,select为函数,column为输入。
        <collection property="students" javaType="ArrayList" ofType="pojo.Student" select="getStudent" column="id"/>
    </resultMap>

3 按照结果嵌套处理

#接口

//查询某一老师的信息
    Teacher getTeachers(int id);

#mapper

<mapper namespace="dao.TeacherMapper">

    <select id="getTeachers" resultMap="Teacher_collection_students">
        select s.id sid,s.name sname,t.name tname,t.id tid from mybatis.student s,mybatis.teacher t where s.tid = t.id and t.id = #{any_tid}
    </select>
    <resultMap id="Teacher_collection_students" type="pojo.Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--如果是基本类型或自定义类型,用javaType。
        如果是集合中的泛型信息,则用ofType获取。-->
        <collection property="students" ofType="pojo.Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
    
</mapper>

 

12.3 小结

1.关联-association【多对一】

2.集合-collection【一对多】

3.javaType & ofType

  • javaType指定实体类中属性的类型

  • ofType指定映射到List或者集合中的pojo类型,泛型中的约束类型。

面试高频:

Mysql引擎

InnoDB底层原理

索引

索引优化

 

13 动态SQL

hello所谓的动态sql,本质还是sql语句,只是我们可以在sql层面,去执行一个逻辑代码。

概述:

if是为了解决where后多个and或or情况的问题。0个~多个。

choose则从多个条件里选择一个。 1个。choose<when,when...otherwise>

where则取代了sql语句里的where,当子句为where后第一个子句时,自动去除子句里的"and"或"or"。

foreach则是处理需要对集合进行遍历(尤其是在构建 IN 条件语句的时候)的情况。

 

1 环境搭建

1.1 基本环境搭建

Blog实体类

@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;//属性名和字段名不一致
    private int views;
}

Blog数据表

create table `blog`(
  `id` varchar(50) not null comment '博客id',
  `title` varchar(100) not null comment '博客标题',
  `author` varchar(30) not null comment '博客作者',
  `create_time` datetime not null comment '创建时间',
  `views` int(30) not null comment '浏览量'
)engine =INNODB default charset=utf8;

自动产生id的工具类

package utils;
public class IDutils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
}

mapper映射文件BlogMapper.xml以及注册进核心配置文件

<?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">
<!--namespace绑定一个对应的mapper接口-->
<mapper namespace="dao.BlogMapper">
</mapper>

注册进核心配置文件mybatis-config.xml
<mapper resource="mapper/BlogMapper.xml"/>

1.2 插入点数据

#接口

package dao;
import pojo.Blog;
import java.util.List;

public interface BlogMapper {
    int addBlog(Blog blog);
}

#mapper

<insert id="addBlog" parameterType="pojo.Blog">
    insert into mybatis.blog(id, title, author, create_time, views)
    values(#{id},#{title},#{author},#{createTime},#{views});
</insert>

#Test插入数据

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Blog blog = new Blog();

    blog.setId(IDutils.getId());
    blog.setTitle("javaweb基础");
    blog.setAuthor("xiaoyang");
    blog.setCreateTime(new Date());
    blog.setViews(9999);
    blogMapper.addBlog(blog);

    blog.setId(IDutils.getId());
    blog.setTitle("java如此简单");
    blogMapper.addBlog(blog);

    blog.setId(IDutils.getId());
    blog.setTitle("mybatis如此简单");
    blogMapper.addBlog(blog);

    sqlSession.close();
}

 

2 IF

#接口

List<Blog> queryBlogIf(Map map);

#mapper

<!--说明:title和author都是可选项-->
<select id="queryBlogIf" parameterType="map" resultType="pojo.Blog">
    select * from mybatis.blog where 1=1
    <if test="title != null">
        and title like "%"#{title}"%"
    </if>
    <if test="author != null">
        and author=#{author}
    </if>
</select>

#Test

    //只有author
    //有author,有title

@Test
public void test1(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);

    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put("author", "xiaoyang");
    List<Blog> blogs = blogMapper.queryBlogIf(hashMap);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    System.out.println("========================");

    HashMap<String, String> hashMap1 = new HashMap<>();
    hashMap1.put("author", "xiaoyang");
    hashMap1.put("title", "java");
    List<Blog> blogs1 = blogMapper.queryBlogIf(hashMap1);
    for (Blog blog : blogs1) {
        System.out.println(blog);
    }
}

#输出

 

 

 

3 Choose(when,otherwise)

#接口

List<Blog> queryBlogChoose(Map map);

#mapper

 <select id="queryBlogChoose" parameterType="map" resultMap="To  Blog">
     select * from mybatis.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>
<resultMap id="ToBlog" type="pojo.Blog">
    <result property="createTime" column="create_time"/>
</resultMap>

#Test

    //只有author
    //有author,有title
    //author和title为空,则根据views取值

@Test
public void test1(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);

    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put("author", "xiaoyang");
    List<Blog> blogs = blogMapper.queryBlogChoose(hashMap);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    System.out.println("========================");

    HashMap<String, String> hashMap1 = new HashMap<>();
    hashMap1.put("author", "xiaoyang");
    hashMap1.put("title", "java");
    List<Blog> blogs1 = blogMapper.queryBlogChoose(hashMap1);
    for (Blog blog : blogs1) {
        System.out.println(blog);
    }
    System.out.println("========================");

    HashMap<String, Integer> hashMap2 = new HashMap<>();
    hashMap2.put("views", 9999);
    List<Blog> blogs2 = blogMapper.queryBlogChoose(hashMap2);
    for (Blog blog : blogs2) {
        System.out.println(blog);
    }

}

#输出

 

 

 

4 trim(where,set)

where在查询语句(select .. where)里出现,会智能去除无关"and"/"or"

set在更新语句(update .. set)里出现,会智能去除无关逗号","

 

 

 

trim是定制类如where,set这样的工具

 

 

网上很多关于trim的实现

 

5 forEach

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  //("kitty","lili","bob","xiaoyang")
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

例如:

查询id为1,2,5的数据

sql为:

select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=5)
select * from mybatis.blog where id in (1,2,5)

#接口

List<Blog> queryBlogForeach(Map map);

#mapper

/*select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=3)*/
<select id="queryBlogForeach" parameterType="map" resultMap="ToBlog">
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="_id" open="and (" close=")" separator=" or ">
            id=#{_id}
        </foreach>
    </where>
</select>
或者
/*select * from mybatis.blog where id in (1,2,4,5)*/
...
        <foreach collection="ids" item="_id" open="id in(" close=")" separator=",">
            #{_id}
        </foreach>
...

#Test

@Test
public void test4(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap<>();
    ArrayList<Integer> ids = new ArrayList<>();
    //1.什么都不加->全部结果
    //2.add(1,2,5),查询id=(1,2,5)的数据
    ids.add(1);ids.add(2);ids.add(5);
    map.put("ids", ids);


    List<Blog> blogs = blogMapper.queryBlogForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
}

#结果

 

 

 

 

6 sql片段

功能:将sql片段提取出来,方便复用。

使用:

  1. 用sql标签抽取公共的部分

  2. 在需要使用的地方用include标签引用即可

    比如:

    <sql id="Blog-If-tile-author">
        <if test="title != null">
            and title like "%"#{title}"%"
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </sql>
    
    <select id="queryBlogIf" parameterType="map" resultMap="ToBlog">
        select * from mybatis.blog
        <where>
            <include refid="Blog-If-tile-author"/>
        </where>
    </select>

注意:

  1. 最好基于单表来定义SQL片段

  2. 不要存在where/set标签

建议:

  1. 先在mysql里测试准确的sql,再改造成mybatis动态sql。

 

 

14 缓存

Mybatis默认缓存:

Mybatis默认定义了两级缓存:一级缓存和二级缓存

  • 一级缓存默认开启。SqlSession级,也称本地缓存

  • 二级缓存需要手动配置开启。nameSpace级,也称全局缓存。

  • 为了提供拓展性,Mybaits定义了缓存接口Cache。可以通过实现接口Cache来自定义二级缓存。

配置:

一级缓存无需配置,可手动关闭。

二级缓存默认关闭

全局缓存默认开启,允许每个namespace配置自己的二级缓存。可在mybatis核心配置文件关闭。

 

14.1 一级缓存

1 测试

@org.junit.Test
    public void test4(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

        //查找不会更新缓存  teacher1==teacher
        Teacher teacher = mapper.getTeachers2(1);
        Teacher teacher1 = mapper.getTeachers2(1);
        System.out.println(teacher1 == teacher);
        //增删改会更新缓存  teacher2 != teacher
        mapper.updateById(1);
        Teacher teacher2 = mapper.getTeachers2(1);
        System.out.println(teacher2 == teacher);

        sqlSession.close();
    }

结果

Opening JDBC Connection
Created connection 2051853139.
......
true
......
false
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7a4ccb53]
Returned connection 2051853139 to pool.

2 手动清理缓存

sqlsession.clearCache();

上面的例子

//查找不会更新缓存  teacher1==teacher。但是如果在两次查询中手动清理缓存,则  teacher1 != teacher
Teacher teacher = mapper.getTeachers2(1);
sqlSession.clearCache();
Teacher teacher1 = mapper.getTeachers2(1);

 

14.2 二级缓存

1某个mapper(namespace)里开启二级缓存

在这个mapper的映射文件中加入

<cache/>

效果【一级缓存默认开启,且不能关闭】

 

提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。

提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

cache标签拥有属性

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

注意 开启二级缓存的namespace空间用到的实体类必须序列化

2 然而还是 演示失败!!!!!!

false

3 全局缓存开关

所有二级缓存的总开关。

核心配置文件里配置,默认开启。

提示 上一节中对缓存的配置(如清除策略、可读或可读写等),不能应用于自定义缓存。

 

 

3 自定义缓存

ehcache,redis(hot)

 

 

 

 

posted @ 2020-06-17 23:58  p0pl4r  阅读(250)  评论(0编辑  收藏  举报