MyBatis学习笔记

一款优秀的持久层(DAO)框架

1.第一个MyBatis程序

思路:

  • 搭建环境
  • 导入Mybatis
  • 编写代码
  • 测试

image-20210608215210674

1.1 sqlSession工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.codelorin.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
* @author CodeLorin
* @date 2021/6/8 20:48
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;

static {

try {
//获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}

}
//返回值sqlSession拥有所有面向数据库执行sql的所有方法
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}

1.2 实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.codelorin.pojo;

/**
* @author CodeLorin
* @date 2021/6/8 20:54
*/
public class User {
private int id;
private String name;
private String pwd;

public User() {
}

public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}


public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}

1.3 DAO层接口

1
2
3
4
5
6
7
8
9
10
package com.codelorin.dao;

import com.codelorin.pojo.User;

import java.util.List;

public interface UserMapper {
List<User> getUserList();
}

1.4 mybatisMapper配置文件(接口实现类)

1
2
3
4
5
6
7
8
9
10
11
12
<?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">
<!--绑定一个对应的DAO/Mapper接口-->
<mapper namespace="com.codelorin.dao.UserMapper">
<!-- 查询语句,id对应接口中的方法名称 -->
<select id="getUserList" resultType="com.codelorin.pojo.User">
select * from test.user
</select>
<!-- -->
</mapper>

1.5 mybatis数据库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 数据库核心配置文件 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>

<!-- 每一个mapper.xml都需要在mybatis核心配置文件中配置 -->
<mappers>
<mapper resource="com/codelorin/dao/UserMapper.xml"/>
</mappers>
</configuration>

1.6 junit测试

  • 注意

    org.apache.ibatis.binding.BindingException: Type interface com.codelorin.dao.UserDao is not known to the MapperRegistry.

    资源导出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <build>
    <resources>
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>true</filtering>
    </resource>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>true</filtering>
    </resource>
    </resources>
    </build>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.codelorin.dao;

import com.codelorin.pojo.User;
import com.codelorin.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
* @author CodeLorin
* @date 2021/6/8 21:06
*/
public class UserDaoTest {
@Test
public void test() {
//1.获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2. 方式一:getMapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
//2. 方式二 不推荐使用:
List<User> userList1 = sqlSession.selectList("com.codelorin.dao.UserMapper.getUserList");

for (User user : userList1) {
System.out.println(user);
}

sqlSession.close();

}
}

2.CRUD实现

1.namespace

包名要和dao/mapper接口包名一致

2.select

选择,查询

  • id 就是对应namespace中的方法名

  • resultType: sql语句执行的返回值

  • parameteType: 参数类型

2.1 mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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">
<!--绑定一个对应的DAO/Mapper接口-->
<mapper namespace="com.codelorin.dao.UserMapper">
<!-- 查询语句,id对应接口中的方法名称 -->
<select id="getUserList" resultType="com.codelorin.pojo.User">
select *
from user
</select>
<select id="getUserById" parameterType="int" resultType="com.codelorin.pojo.User">
select *
from user
where id = #{id}
</select>
<insert id="addUser" parameterType="com.codelorin.pojo.User">
insert into user (id, name, pwd)
values (#{id}, #{name}, #{pwd})
</insert>
<update id="updateUser" parameterType="com.codelorin.pojo.User">
update user
set name = #{name},
pwd=#{pwd}
where id = #{id}
</update>
<delete id="delUser" parameterType="int">
delete
from user
where id = #{id}
</delete>
</mapper>

2.2 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.codelorin.dao;

import com.codelorin.pojo.User;

import java.util.List;

public interface UserMapper {
//获取全部用户
List<User> getUserList();

//根据id查询用户
User getUserById(int id);

//insert 一个用户
int addUser(User user);

//修改用户
int updateUser(User user);
//删除用户
int delUser(int id);

}

2.3 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.codelorin.dao;

import com.codelorin.pojo.User;
import com.codelorin.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
* @author CodeLorin
* @date 2021/6/8 21:06
*/
public class UserMapperTest {
@Test
public void test() {
//1.获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2. 方式一:getMapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
//2. 方式二 不推荐使用:

for (User user : userList) {
System.out.println(user);
}

sqlSession.close();

}

@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User use = mapper.getUserById(1);
System.out.println(use);
sqlSession.close();
}


@Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(5, "isxxl11", "codexxl"));
System.out.println(res);
//提交事务
sqlSession.commit();
sqlSession.close();
}

@Test
public void updateUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(5, "hahah", "xxl"));
//提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void delUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.delUser(5);
//提交事务
sqlSession.commit();
sqlSession.close();
}
}

3.万能map

当实体类或字段过多可以使用 id为主键自动增加使用(个人理解)

1
int addUser2(Map<String, Object> map);
1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void addUser2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("userId", 10);
map.put("username", "xxl");
map.put("password", "hahah");
mapper.addUser2(map);
//提交事务
sqlSession.commit();
sqlSession.close();
}
1
2
3
4
<insert id="addUser2" parameterType="map">
insert into user (id, name, pwd)
values (#{userId}, #{username}, #{password})
</insert>

4.模糊查询

在参数中拼接%

1
"%xx%"

sql语句中拼接

1
2
3
4
5
<select id="getUserLike" resultType="com.codelorin.pojo.User">
select *
from user
where name like "%"#{value}"%"
</select>

5.配置解析

1.核心配置文件

mybatis-config.xml

5.1.1 environment

1
2
3
4
5
6
7
8
9
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>

id:名称

type:事务管理器(jdbc | managed)

pooled:数据源的配置(UNPOOLED|POOLED|JNDI)

5.1.2 properties(属性)

可以通过它读取一些配置

1
<properties resource="db.properties"/>

5.1.3 类型别名

为java类型设置一个短的名字

存在的意义仅在于用来减少类完全限定名的冗余

1
2
3
4
5
<!-- 别名   -->
<typeAliases>
<typeAlias type="com.codelorin.pojo.User" alias="User"/>
<package name="com.codelorin.pojo"/>
</typeAliases>

typeAlias:一一对应

package:制定一个包下,它会自动搜索,但是使用的时候使用名字的小写

注解:@Alias()

5.1.4 设置(settings)

  • 日志

  • 其他配置

    • typeHandlers(类型处理器)

    • objectFactory(对象工厂)

    • plugins(插件)

      • mybatis-core

      • mybatis-plus

5.1.5 映射器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
```xml
<mappers>
<mapper resource="com/codelorin/dao/UserMapper.xml"/>
</mappers>
```

```xml
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
```

```xml
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
```

6.生命周期和作用域

生命周期和作用域十分重要,错误使用会对严重的并发问题

image-20210609154519888

6.1 sqlSessinFactoryBuilder

一点创建了sqlSessionFactory,就不再需要它了

局部变量

6.2 sqlSessionFactory

数据库连接池

一旦创建就在程序运行期间一直存在,没有任何理由丢弃它或者重新创建另外一个实例

6.3 sqlSession

连接到连接池的一个请求

需要开启和关闭,用完之后立刻关闭

7.解决属性名和字段名不一致的问题

  • 数据库sql使用as

  • resultMap(结果集映射)

    1
    2
    3
    4
    5
    6
    <resultMap id="UserMap" type="User">
    # column数据库字段,property实体类字段
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
    </resultMap>

8.日志

8.1日志工厂

  • STDOUT_LOGGING
1
2
3
4
5
6
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
  • LOG4J

导入包

1
2
3
4
5
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/sys.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yyyy-MM-dd hh:mm:ss}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.codelorin.dao;

import com.codelorin.pojo.User;
import com.codelorin.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;

import org.apache.log4j.Logger;
import org.junit.Test;

import java.util.List;

/**
* @author CodeLorin
* @date 2021/6/9 15:00
*/
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);



@Test
public void log4jTest() {
logger.info("info:进入了log4jTest方法");
logger.debug("debug:进入了log4jTest方法");
logger.error("error:进入了log4jTest方法");
}
}

9.分页limit

1
2
3
select * from user limit i,n
i : 为查询结果的索引值(默认从0开始);
n : 为查询结果返回的数量
  • UserMapper
1
2
//分页
List<User> getUserByLimit(Map<String, Integer> map);
1
2
3
<select id="getUserByLimit" parameterType="map" resultType="user">
select * from user limit #{startIndex},#{pageSize}
</select>
  • 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Test
    public void limitTest() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<>();
    map.put("startIndex", 0);
    map.put("pageSize", 2);
    List<User> userList = mapper.getUserByLimit(map);
    for (User user : userList) {
    System.out.println(user);
    }

    sqlSession.close();
    }

9.1 RowBounds分页

java层面实现的分页,了解就行

9.2 分页插件

PageHelper

10.使用注解开发

使用反射获取注解简化开发

  • openSession(true)即可自动提交事务

  • 注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //获取全部用户
    @Select("select * from user")
    List<User> getUserList();

    @Select("select * from user where id = #{id}")
    User getUserById(@Parm("id") int id);

    @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{pwd})")
    int addUser(User user)

    @Update("update user set name = #{name},pwd=#{pwd} where id = #{id}")
    int updateUser(User user)

    @Delete("delete from user where id = #{id}")
    int deleteUser(int id)
  • 配置文件

    1
    2
    3
    <mappers>
    <mapper class="com.codelorin.dao.UserMapper"/>
    </mappers>

11.mybatis执行流程剖析

  • resources获取全局配置文件
  • 实例化SQLSessionFactoryBuilder构造器
  • 解析配置文件流XMLConfigBuilder
  • Configuration所有的配置信息
  • SqlSessionFactory实例化
  • transactional事务管理
  • 创建executor执行器
  • 创建sqlSession
  • 实现curd,错误就回滚
  • 提交事务
  • 关闭

12.Lombok

  • 安装Lombok插件
  • maven导入jar包
  • 实体类上加注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@UtilityClass

@Data 无参 get set tostring hashcode equals
@AllArgsConstructor and @NoArgsConstructor 有参无参


13.多对一的处理

  • 按照查询嵌套查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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">
<!--绑定一个对应的DAO/Mapper接口-->
<mapper namespace="com.codelorin.dao.StudentMapper">

<select id="getStudent" resultType="StudentTeacher">
select *
from student
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 复杂的属性,我们需要单独处理
对象使用association
集合使用collection
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select *
from teacher
where id = #{id}
</select>
</mapper>
  • 按照结果嵌套查询
1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid, s.name sname, t.name tname
from student s,
teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>

14.一对多的处理

  • 按查询嵌套
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="getTeacher2" resultMap="TeacherStudent2">
select *
from teacher
where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<!--javaType="ArrayList" 可以没有-->
<collection property="students" column="id" ofType="Student" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select *
from student
where tid = #{id}
</select>
  • 按结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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">
<!--绑定一个对应的DAO/Mapper接口-->
<mapper namespace="com.codelorin.dao.TeacherMapper">

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

</mapper>

总结

image-20210913001824137

15.动态sql

根据不同的条件生成不同的sql语句

  • if
1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>


</select>
1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap hashMap = new HashMap();
hashMap.put("title", "数据库");
List<Blog> blogs = mapper.queryBlogIf(hashMap);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
  • choose (when, otherwise)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
  • trim (where, set)

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

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)

1
2
3
4
5
6
7
8
9
10
11
12
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
1
2
3
4
5
6
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
  • sql片段,提高复用性
1
2
3
4
5
6
7
8
9
10
    <sql id="if-tile-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>

<include refid="if-tile-author"></include>
  • foreach
1
2
3
4
5
6
7
8
<select id="queryBlogForEach" parameterType="map" resultType="blog">
select * from blog
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>

</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void test() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap hashMap = new HashMap();
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(4);
hashMap.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForEach(hashMap);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}

16.缓存

读写分离,主从复制

xxx

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

sqlSession级别的缓存,只在一次会话中使用.

  • 二级缓存

1.开启全局缓存

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

2.

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

1
<cache/>

这些属性可以通过 cache 元素的属性来修改。比如:

1
2
3
4
5
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

3.测试

我们需要将实体类序列化

1
public class Blog implements Serializable

小结

  • 只要开启了二级缓存,在同一个mapper下有效

  • 所有的数据都放在一级缓存中

  • 当会话提交或者关闭时,才会提交到二级缓存中

    image-20210913022435413

  • 自定义缓存

ehcache

1
2
3
4
5
6
7
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>

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