0%

MyBatis汇总

环境:

  • JDK 1.8
  • MySQL 5.7
  • Maven 3.6.1
  • IDEA

预备知识:

  • JDBC
  • MySQL
  • Java基础
  • Maven
  • Junit

简介

什么是 MyBatis

  • MyBatis 是一款优秀的持久层框架

  • 它支持自定义 SQL、存储过程以及高级映射

  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作

  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录

  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis

  • 2013年11月迁移到Github

如何获得MyBatis?

  • Maven

    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
    </dependency>
  • Github

  • 中文文档

持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电即失
  • 数据库(JDBC),IO文件持久化
  • 生活:冷藏,罐头

为什么需要持久化?

  • 内存太贵
  • 有一些数据要长久保存

持久层

DAO层、Service层、Controller层

  • 完成持久化工作的代码块
  • 界限分明

为什么需要MyBatis?

  • 帮助程序员将数据存入到数据库中
  • 方便
  • 传统的JDBC代码太复杂了。简化。框架。自动化
  • 不用MyBatis也可以。更容易上手
  • 优点:
    • 简单易学
    • 灵活
    • 解除sql与程序代码的耦合
    • 提供映射标签,支持对象与数据库的orm字段关系映射
    • 提供对象关系映射标签,支持对象关系组建维护
    • 提供xml标签,支持编写动态sql
    • (使用的人多)

第一个MyBatis程序

思路:搭建环境 -> 导入MyBatis -> 编写代码 -> 测试。(有疑问可查看MyBatis的中文文档

搭建环境

创建数据库、创建Maven父工程。

搭建数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> create database mypos; #创建数据库mypos
Query OK, 1 row affected (0.00 sec)

mysql> use mypos;
Database changed
mysql> create table `user`( #创建数据表 user
-> `id` int unsigned auto_increment primary key, #无符号int类型,自动增加,主键
-> `name` varchar(20) not null unique, #字符类型,非空,唯一性约束
-> `passwd` varchar(30) default "123456", #字符类型,默认为123456
-> `type` varchar(10) default "common", #该字段记录账户的类型,字符类型,默认为common
-> `time` timestamp default current_timestamp, #该字段记录账户的创建时间,时间戳类型,默认为插入数据时的当前时间
-> `timeup` timestamp default current_timestamp on update current_timestamp#该字段记录账户的修改时间,时间戳类型,默认为插入数据时的当前时间,该条记录被修改时自动更新该字段值
-> )engine=innodb default charset=utf8;#存储引擎为innodb,字符编码为utf-8
Query OK, 0 rows affected (0.02 sec)

# 命令“desc user”可以查看user表的结构

新建项目

  1. 新建一个普通的maven项目,作为父工程
  2. 删除src目录
  3. 导入maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--    导入依赖-->
<dependencies>
<!-- mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

创建一个模块

在父工程中创建一个模块Module。开始编写Mybatis代码。

核心配置文件

编写mybatis的核心配置文件。在resources文件夹下新建配置文件mybatis-config.xml,添加如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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://qsdbl.site:3306/mypos?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
<property name="username" value="jack"/>
<property name="password" value="12345Qsdbl--"/>
</dataSource>
</environment>
</environments>
</configuration>

&amp;是”&”的转义字符。

解决中文乱码、时区问题,参考这篇博客

mybatis工具类

编写mybatis工具类,用于获取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
package com.qsdbl.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;
//SqlSessionFactory -> SqlSession
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用MyBatis第一步:获取SqlSessionFactory对象
String resource = "mybatis-config.xml";//加载resources文件夹下的文件,直接使用文件名即可
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();//添加参数true,执行SQL后会自动提交(增、删、改,需要提交事务,见下标的CRUD)
}
}

编写代码

  • 实体类。对应数据库中的一张表。

  • Mapper接口(对应使用JDBC时的DAO模块)

    1
    2
    3
    4
    5
    6
    7
    package com.qsdbl.mapper;
    import com.qsdbl.bean.User;
    import java.util.List;

    public interface UserMapper {
    List<User> getUserList();
    }
  • 接口实现类由原来的UserMapper实现类转变为一个Mapper配置文件(XML),在该xml文件中只需简单配置sql语句即可。还需要到核心配置文件中进行注册

    Mapper配置文件:namespace,绑定Mapper接口。包名,使用”.”隔开。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?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,绑定一个对应的Dao/Mapper接口(注意包名不要写错)-->
    <mapper namespace="com.qsdbl.mapper.UserMapper">

    <!-- select查询语句。id对应接口中的方法名。resultType声明返回的数据类型(这里写实体类User,而不是getUserList方法返回的List集合-->
    <select id="getUserList" resultType="com.qsdbl.bean.User">
    select * from user
    </select>

    </mapper>

    核心配置文件mybatis-config.xml中进行注册

    1
    2
    3
    4
    5
    6
    <!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册。mappers标签与environments标签同级-->
    <mappers>
    <mapper resource="com/qsdbl/mapper/UserMapper.xml"/>
    <!-- 多个配置文件,可以使用通配符 -->
    <!--<mapper resource="com/qsdbl/mapper/*Mapper.xml"/>-->
    </mappers>

    本例子中,Mapper配置文件放在与Mapper接口同一包内,所以路径(注意,路径使用“/”分隔)为resource="com/qsdbl/mapper/UserMapper.xml",但是需要配置Maven才能正常读取到该文件(才会被一块打包出来),建议放到resources文件夹下,路径可直接使用文件名。

    建议配置文件和接口同名放在同一包下,使用包扫描自动注册。不需要每个配置文件都生动注册。

测试-使用

junit测试

junit测试,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
package com.qsdbl.mapper;
import com.qsdbl.bean.User;
import com.qsdbl.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
//第一步:获取sqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try{
//第二步:执行sql
//方式一:getMapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();

//方式二(不推荐使用)
// List<User> userList = sqlSession.selectList("com.qsdbl.mapper.UserMapper.getUserList");

System.out.println("遍历userList:");
for (User user:userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭sqlSession
sqlSession.close();//关闭代码,最好放在try-catch-finally的finally代码块中
}
}
}

测试结果:

1
2
3
4
5
6
7
8
9
遍历userList:
User{id=1, name='小黑', passwd='123456', type='common', time='2021-01-21 11:41:36.0', timeup='2021-01-21 11:41:36.0'}
User{id=2, name='小红', passwd='123456', type='common', time='2021-01-21 11:42:34.0', timeup='2021-01-21 11:42:34.0'}
User{id=3, name='小花', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
User{id=4, name='小明', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
User{id=5, name='pete', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
User{id=6, name='admin', passwd='123456', type='admin', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}

Process finished with exit code 0

可能遇到的问题

  1. 配置文件没有注册
  2. 绑定接口错误(Mapper配置文件中的namespace)
  3. 方法名不对(Mapper配置文件中的id)
  4. 返回类型不对(Mapper配置文件中的resultType)
  5. Maven导出资源问题

配置文件没有注册

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

MapperRegistry异常,写好Mapper配置文件后还需要在核心配置文件mybatis-config.xml中进行注册(mappers标签与environments标签同级)。

1
2
3
4
<!--    每一个Mapper.xml都需要在MyBatis核心配置文件中注册。mappers标签与environments标签同级-->
<mappers>
<mapper resource="com/qsdbl/mapper/UserMapper.xml"/>
</mappers>

Maven导出资源问题

资源(java文件夹下的xml文件)加载失败的问题。前边UserMapper接口对应的Mapper配置文件由于写在接口同级的包内,所以在打包时可能不会被Maven打包出来,需要在父工程(或当前模块module)的pom.xml文件中添加如下配置(build标签与dependencies标签同级。解决方法参考这篇博客):

1
2
3
4
5
6
7
8
9
10
11
12
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

写在resources文件夹下则不会出现该问题,缺点就是没有与UserMapper接口放在一起看起来清晰明了,不然文件多了会很乱(不过也可以模仿包名创建对应的文件夹)。

若放在resources文件夹下,在核心配置文件中注册使用的路径如下:

1
2
3
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>

属性名/列名冲突

属性名与字段名(数据库列名)不一致。需在核心配置文件中启用一个设置。例如:字段名为“_“连接,属性名为驼峰命名。create_time与createTime。

1
2
3
4
<settings>
<!--是否开启驼峰命名自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

CRUD

增加(Create)、检索(Retrieve)、更新(Update)和删除(Delete)

select – 查询

  • 选择,查询语句

  • id,对应的namespace中的方法名

  • resultType,sql语句执行的返回值类型

  • parameterType,参数类型

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
//parameterType的使用:
//1、UserMapper接口中,新增一个方法,有一个参数name(多个参数,需要使用注解@Param设置参数名。见后边的“使用注解开发”笔记):
User getUserByName(String name);//根据用户名 查询用户

//2、Mapper配置文件中添加如下sql语句(在前边已经在mybatis核心配置文件注册了该文件,所以这里不需要再去注册):
<select id="getUserByName" resultType="com.qsdbl.bean.User" parameterType="String">
select * from user where name = #{name}
</select>

//3、测试类中进行如下测试:
@Test
public void getUserByName(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try{
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserByName("admin");
System.out.println("用户名为admin的用户信息:\n"+user);
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}

//4、测试结果:
用户名为admin的用户信息:
User{id=6, name='admin', passwd='123456', type='admin', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}

Process finished with exit code 0

insert – 增加

  • 注意:
  • 增、删、改,需要提交事务。前边的工具类中openSession()方法添加参数true,会自动提交事务不需要手动提交(下边第19行)。
  • 参数使用引用类型时,Mapper配置文件中的sql语句使用表达式#{引用类型中的字段名}绑定参数。
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
//1、UserMapper接口中,新增一个方法,有一个参数user:    
int addUser(User user);//新增一个用户(成功后返回1)

//2、Mapper配置文件中添加如下sql语句(在前边已经在mybatis核心配置文件注册了该文件,所以这里不需要再去注册)。#{name}中的name,要与实体类User中的字段名name对应上:
<insert id="addUser" parameterType="com.qsdbl.bean.User">
insert into user(name,passwd,type) values(#{name},#{passwd},#{type})
</insert>

//3、测试类中进行如下测试:
@Test
public void addUser(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try{
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User("alger","1111","admin"));
if (res > 0){
System.out.println("用户添加成功!");
}
sqlSession.commit();//提交事务(增、删、改,需要提交事务)
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}

//4、测试结果:
用户添加成功!

Process finished with exit code 0

扩展:插入数据后,返回id,id是数据库自增字段。(摘自SpringBoot项目

1
2
3
4
//插入(新增)账户
@Insert("INSERT INTO account(username,password,sex,birthday,tel,mail,icon)VALUES(#{username},#{password},#{sex},#{birthday},#{tel},#{mail},#{icon})")//el表达式
@Options(useGeneratedKeys = true, keyProperty = "id")//插入数据后,将产生的id放到account对象中
public void insert(Account account);

update – 修改

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
//1、UserMapper接口中,新增一个方法,有一个参数user:    
int updateUser(User user);//修改用户(成功后返回1)

//2、Mapper配置文件中添加如下sql语句(在前边已经在mybatis核心配置文件注册了该文件,所以这里不需要再去注册)。#{name}中的name,要与实体类User中的字段名name对应上:
<update id="updateUser" parameterType="com.qsdbl.bean.User">
update user set passwd=#{passwd},type=#{type} where name=#{name}
</update>

//3、测试类中进行如下测试:
@Test
public void updateUser(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try{
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.updateUser(new User("晓彤","000000"));
if (res > 0){
System.out.println("用户修改成功!");
}
sqlSession.commit();//提交事务(增、删、改,需要提交事务)
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}

//4、测试结果:
用户修改成功!

Process finished with exit code 0

delete – 删除

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
//1、UserMapper接口中,新增一个方法,有一个参数name:    
int deleteUser(String name);//删除用户(根据用户名。成功后返回1)

//2、Mapper配置文件中添加如下sql语句(在前边已经在mybatis核心配置文件注册了该文件,所以这里不需要再去注册)。#{name}中的name,要与实体类User中的字段名name对应上:
<delete id="deleteUser" parameterType="String">
delete from user where name=#{name}
</delete>

//3、测试类中进行如下测试:
@Test
public void deleteUser(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try{
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.deleteUser("晓彤");
if (res>0){
System.out.println("用户删除成功!");
}
sqlSession.commit();//提交事务(增、删、改,需要提交事务)
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}

//4、测试结果:
用户删除成功!

Process finished with exit code 0

Map传递参数

假设,我们的实体类或者数据库中的表字段或者参数过多,我们可以考虑使用Map而不是一定要使用实体类。

  • Map传递参数,直接在sql中取出key即可。parameterType="Map"
  • 对象(实体类)传递参数,直接在sql中取对象的属性即可。parameterType="类的全限定名"
  • 只有一个基本类型参数的情况下,可以直接在sql中取到。
  • 多个参数可以考虑使用Map,或者注解。
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
//1、UserMapper接口中,新增一个方法,有一个参数map:    
int addUser2(Map<String,Object> map);//增加用户。参数使用map

//2、Mapper配置文件中添加如下sql语句(在前边已经在mybatis核心配置文件注册了该文件,所以这里不需要再去注册)。参数使用map,#{username}中写相应的键名,与实体类无关:
<insert id="addUser2" parameterType="Map">
insert into user(name,passwd,type) values(#{username},#{userpasswd},#{usertype})
</insert>

//3、测试类中进行如下测试:
@Test
public void addUser2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try{
UserMapper mapper = sqlSession.getMapper(UserMapper.class);

Map<String,Object> map = new HashMap<String, Object>();
map.put("username","superAdmin");//map,键值对
map.put("userpasswd","1111");
map.put("usertype","admin");

int res = mapper.addUser2(map);
if (res > 0){
System.out.println("用户添加成功!");
}
sqlSession.commit();//提交事务(增、删、改,需要提交事务)
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}

//4、测试结果:
用户添加成功!

Process finished with exit code 0

模糊查询

Java代码中拼接通配符%%

1
2
3
4
//java代码:
List<User> userList = userMapper.getUserLike("%小%");
//sql语句如下:
select * from user where name like #{value}

在sql中使用通配符%%

1
2
3
4
5
6
7
//java代码:
List<User> userList = userMapper.getUserLike("小");

//sql语句如下:
select * from user where name like concat('%',#{value},'%')
//或:
select * from user where name like "%"#{value}"%"

代码:

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
//1、UserMapper接口中,新增一个方法,有一个参数value:    
List<User> getUserLike(String value);//模糊查询

//2、Mapper配置文件中添加如下sql语句(在前边已经在mybatis核心配置文件注册了该文件,所以这里不需要再去注册)。参数使用map,#{value}中写相应的键名,与实体类无关:
<select id="getUserLike" parameterType="String" resultType="com.qsdbl.bean.User">
select * from user where name like concat('%',#{value},'%')
</select>

//3、测试类中进行如下测试:
@Test
public void getUserLike(){
//第一步:获取sqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();

try{
//第二步:执行sql
//方式一:getMapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserLike("小");

System.out.println("模糊查询,遍历userList:");
for (User user:userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭sqlSession
sqlSession.close();//关闭代码,最好放在try-catch-finally的finally代码块中
}
}

//4、测试结果:(查询出了所有名字中包含“小”字的账户信息)
模糊查询,遍历userList:
User{id=1, name='小黑', passwd='123456', type='common', time='2021-01-21 11:41:36.0', timeup='2021-01-21 11:41:36.0'}
User{id=2, name='小红', passwd='123456', type='common', time='2021-01-21 11:42:34.0', timeup='2021-01-21 11:42:34.0'}
User{id=3, name='小花', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
User{id=4, name='小明', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}

Process finished with exit code 0

配置解析

核心配置文件

  • 官方建议的核心配置文件名为mybatis-config.xml

  • MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)

环境配置

MyBatis可以配置成适应多种环境(environments),但是每个SqlSessionFactory实例只能选择一种环境,所以只适用于在测试时下快速切换环境。

注意一些关键点:

  • 默认使用的环境 ID(environments标签。默认:default=”development“。通过设置default对应的环境ID来切换所要使用的环境)。
  • 每个 environment 元素定义的环境 ID(environment标签。默认:id=”development“。不同环境,不同的数据库)。
  • 事务管理器的配置(environment标签内的transactionManager标签。默认:type=”JDBC“)。
    • 在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”)
  • 数据源的配置(environment标签内的dataSource标签。默认:type=”POOLED“)。
    • 有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”)
    • UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。
    • POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。
    • JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

属性

我们可以通过properties属性来实现引用配置文件(解耦)。

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。

编写一个配置文件db.properties,将一些参数写在另一个文件中:

1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://qsdbl.site:3306/mypos?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username=jack
password=12345Qsdbl--

在核心配置文件中引入(db.properties放在resources文件夹下,故可以直接通过文件名读取到):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--    引入外部配置文件-->
<properties resource="db.properties">
<property name="password" value="12345"/>
<!-- 可以在properties标签内写配置信息,但是外部的配置文件优先级更高。-->
</properties>


<!-- 外部配置文件的使用如下,通过${键名}获取到对应的值。注意与Mapper配置文件中#{键名}的区别-->
<environments default="mydevelopment-mysql">
<environment id="mydevelopment-mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
  • 可以直接引入外部配置文件。properties标签的resource属性配置外部文件的路径。
  • 可以在properties标签内增加一些属性配置。
  • 如果存在相同的字段,则优先使用外部配置文件的。
  • 注意:properties标签要写在configuration标签的最前边(约定大于配置。在xml文件中,标签是可以规定其顺序的)。

别名

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

方式一:

直接给某个类(全限定名)起别名

1
2
3
4
5
6
7
8
9
<!--    可以给实体类 起别名-->
<typeAliases>
<typeAlias type="com.qsdbl.bean.User" alias="User"/>
</typeAliases>

<!-- 使用(在UserMapper.xml配置文件中的模糊查询,返回结果集类型由长长的全限定名改成别名User:-->
<select id="getUserLike" parameterType="String" resultType="User">
select * from user where name like concat('%',#{value},'%')
</select>

方式二:

指定一个包名,MyBatis会在包名下搜索需要的Javabean,默认的别名为这个类 类名首字母小写(实测大写也行)。

1
2
3
4
5
6
7
8
9
<!--    可以给实体类 起别名-->
<typeAliases>
<package name="com.qsdbl.bean"/>
</typeAliases>

<!-- 使用(在UserMapper.xml配置文件中的模糊查询,返回结果集类型由长长的全限定名改成别名user:-->
<select id="getUserLike" parameterType="String" resultType="user">
select * from user where name like concat('%',#{value},'%')
</select>
  • 在实体类比较少的时候,使用第一种方式。可以自定义别名。

  • 在实体类比较多的时候,使用第二种方式。不可以自定义别名,但是可以通过注解实现。

    • 在实体类上使用该注解:@Alias("别名")
  • 注意:typeAliases标签要写在properties标签的后边(约定大于配置)。

设置

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
1
2
3
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

其他

映射器

映射器的作用是告诉 MyBatis 到哪里去找到定义好的SQL语句。可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。官方文档中有4种方式

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
<!-- 使用相对于类路径的资源引用。放在resources文件夹下,可以直接使用文件名。下边例子中,xml配置文件放在与mapper接口同一包下。推荐该方式 -->
<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>

<!-- 使用映射器接口实现类的完全限定类名。要求配置文件与mapper接口处于同一包下且同名。不建议 -->
<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>

生命周期和作用域

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

SqlSessionFactoryBuilder

  • 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
  • 最佳作用域是方法作用域。局部方法变量

SqlSessionFactory

  • 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • 最佳作用域是应用作用域。单例模式或者静态单例模式
  • 可以理解为数据库连接池

SqlSession

  • 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
  • 可以理解为连接到连接池的一个请求,使用完后需要关闭,否则资源被占用。

这里面的每一个Mapper就代表一个具体的业务。

结果集映射

  • 结果集映射可以解决属性名字段名不一致的问题。实体类中的属性名与数据表中的字段名。
  • 可以对复杂的查询结果(一对多查询)进行结果集映射。例如:查询某个教师信息同时查询所教的班级。所教的班级有多个,可以使用一个List集合保存(教师对象中添加一个List变量)。将一个个班级信息保存到教师对象的List变量中就需要使用结果集映射来解决。案例见这篇博客

例子(属性名字段名不一致):数据库中,字段名为pwd。实体类中属性名写成了password。

思路一:修改sql,起别名,例如:select id,pwd as password from user

思路二:结果集映射resultMap,有点类似起别名。在Mapper配置文件中对不一致的属性名字段名进行映射即可,不需要全部映射(相同的不需要)。

1
2
3
4
5
6
7
8
9
10
11
<!--    结果集映射-->
<resultMap id="userMap" type="com.qsdbl.bean.User">
<!-- column为数据库中的字段名,property为实体类中的属性名 -->
<result column="pwd" property="password"/>
</resultMap>


<!-- resultType="com.qsdbl.bean.User"更改为resultMap="userMap",userMap为上边结果集映射标签的id值-->
<select id="getUserByName" resultMap="userMap" parameterType="String">
select * from user where name = #{name}
</select>
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • resultMap 元素是 MyBatis 中最重要最强大的元素。
  • resultMap不仅仅可以解决属性/字段名不一致的问题,还可以实现多表查询等功能。
  • 更多使用方法,可以参考官网的文档

日志

如果数据库操作出现了异常,我们需要排错。日志就是最好的助手。

以前:sout、debug

现在:日志工厂

设置名 描述 有效值 默认值
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

在MyBatis中具体使用哪个日志实现,在设置中设定

STDOUT_LOGGING

1
2
3
4
5
<!--设置中,开启日志-->
<settings>
<!--标准的日志工厂实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

在MyBatis核心配置文件中开启即可使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Class not found: org.jboss.vfs.VFS
JBoss 6 VFS API is not available in this environment.
...
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1263793464.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4b53f538]
==> Preparing: select * from user where name = ?
==> Parameters: admin(String)
<== Columns: id, name, passwd, type, time, timeup
<== Row: 6, admin, 123456, admin, 2021-01-21 11:42:35.0, 2021-01-21 11:42:35.0
<== Total: 1
用户名为admin的用户信息:
User{id=6, name='admin', passwd='123456', type='admin', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4b53f538]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4b53f538]
Returned connection 1263793464 to pool.

Process finished with exit code 0

LOG4J

Log4j是什么?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
  1. 导入依赖(pom.xml文件)

    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>

2021-12-20日,Apache Log4j2连续曝光了三个漏洞。官方已发布 2.17.0 版本修复漏洞,注意不要使用比2.17.0低的版本。(受影响版本为2.0~2.14,此处使用的是1.2不受影响)

  1. log4j.properties(放在resources文件夹下),各个参数的含义可参考这篇博客

    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
    #将等级为DEBUG(大于等于)的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    #日志级别有 ALL < TRACE < DEBUG <INFO <WARN <ERROR <FATAL <OFF
    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
    #注意这个日志文件输出位置,项目根目录下的”log/log4j.log“
    log4j.appender.file.File=./log/log4j.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{yy-MM-dd}][%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
  2. 配置log4j为日志的实现(在核心配置文件中)

    1
    2
    3
    4
    5
    <!--设置中,开启日志-->
    <settings>
    <!--LOG4J-->
    <setting name="logImpl" value="LOG4J"/>
    </settings>
  3. 简单使用

    1. 在要使用Log4j的类中,导入包import org.apache.log4j.Logger;

    2. 日志对象,参数为当前类的class

      1
      2
      static Logger logger = Logger.getLogger(当前类.class);
      //或者在类上使用注解 @Log4j(Lombok提供的注解)
    3. 日志级别

      1
      2
      3
      4
      5
      logger.info("info:普通info信息。可代替sout使用");
      logger.debug("debug:debug信息");
      logger.error("error:error信息");

      //使用注解@Log4j时,使用 log.info("普通info信息") 输出日志(变量log)

分页

为什么要分页?

  • 减少数据的处理量

Limit

使用limit分页

1
2
3
4
select * from user limit startIndex,pageSize;

#第一个为下标,下标从0开始。第二个参数为查询数量。
select * from user limit 0,5;

使用MyBatis实现分页,核心SQL:

  1. 接口

    1
    2
    //分页
    List<User> getUserByLimit(Map<String,Integer> map);
  2. mapper.xml

    1
    2
    3
    4
    <!--    分页-->
    <select id="getUserByLimit" parameterType="map" resultType="user">
    select * from user limit #{startIndex},#{pageSize};
    </select>
  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
    @Test
    public void getUserByLimit(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",0);//开始页码(下标
    map.put("pageSize",4);//页面大小
    List<User> userList = mapper.getUserByLimit(map);
    System.out.println("分页查询:");
    for (User user:userList) {
    System.out.println(user);
    }

    sqlSession.close();
    }

    //结果(前边开启了Log4j日志,所以输出比较多):
    ...
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1634132079.
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6166e06f]
    [com.qsdbl.mapper.UserMapper.getUserByLimit]-==> Preparing: select * from user limit ?,?;
    [com.qsdbl.mapper.UserMapper.getUserByLimit]-==> Parameters: 0(Integer), 4(Integer)
    [com.qsdbl.mapper.UserMapper.getUserByLimit]-<== Total: 4
    分页查询:
    User{id=1, name='小黑', passwd='123456', type='common', time='2021-01-21 11:41:36.0', timeup='2021-01-21 11:41:36.0'}
    User{id=2, name='小红', passwd='123456', type='common', time='2021-01-21 11:42:34.0', timeup='2021-01-21 11:42:34.0'}
    User{id=3, name='小花', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
    User{id=4, name='小明', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6166e06f]
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6166e06f]
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1634132079 to pool.

    Process finished with exit code 0

RowBounds

RowBounds实现分页。MyBatis官方不建议(已废弃)

不使用SQL实现分页

  1. 接口

    1
    2
    //分页
    List<User> getUserByRowBounds();
  2. mapper.xml。sql语句为查询所有数据。

    1
    2
    3
    4
    <!--    分页-->
    <select id="getUserByRowBounds" resultType="user">
    select * from user;
    </select>
  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
    @Test
    public void getUserByRowBounds(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();

    //通过Java代码层面实现分页
    RowBounds rowBounds = new RowBounds(0, 4);
    List<User> userList = sqlSession.selectList("com.qsdbl.mapper.UserMapper.getUserByRowBounds",null,rowBounds);

    System.out.println("分页查询:");
    for (User user:userList) {
    System.out.println(user);
    }

    sqlSession.close();
    }

    //结果(前边开启了Log4j日志,所以输出比较多):
    ...
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1579526446.
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e25a92e]
    [com.qsdbl.mapper.UserMapper.getUserByRowBounds]-==> Preparing: select * from user;
    [com.qsdbl.mapper.UserMapper.getUserByRowBounds]-==> Parameters:
    分页查询:
    User{id=1, name='小黑', passwd='123456', type='common', time='2021-01-21 11:41:36.0', timeup='2021-01-21 11:41:36.0'}
    User{id=2, name='小红', passwd='123456', type='common', time='2021-01-21 11:42:34.0', timeup='2021-01-21 11:42:34.0'}
    User{id=3, name='小花', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
    User{id=4, name='小明', passwd='123456', type='common', time='2021-01-21 11:42:35.0', timeup='2021-01-21 11:42:35.0'}
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e25a92e]
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e25a92e]
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1579526446 to pool.

    Process finished with exit code 0

分页插件

了解分页插件pagehelper,案例见这篇博客

使用注解开发

面向接口编程

  • 大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
  • 根本原因:==解耦==,可扩展,提高复用。分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
  • 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的,在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了
  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解

  • 接口从更深层次的理解,应是定义(规范,约束)与实现的分离。
  • 接口的本身反映了系统设计人员对系统的抽象理解
  • 接口应有两类:
    • 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class)
    • 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface)
  • 一个抽象体可能有多个抽象面。抽象体与抽象面是有区别的

三个面向区别

  • 面向对象是指我们考虑问题时,以对象为单位,考虑它的属性及方法
  • 面向过程是指我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构

使用注解开发

本质:反射机制实现

底层:动态代理

  • 增 - @Insert
  • 删 - @Delete
  • 改 - @Update
  • 查 - @Select
  • @Param(“字段名”)注解
    • 基本类型的参数或者String类型,需要加上
    • 引用类型不需要加
    • 如果只有一个基本类型的话,可以忽略,但是建议都加上
    • 我们在SQL中引用的就是在@Param注解中设定的字段名
    • #{}与${}的区别,前者能够很大程度上防止SQL注入。

示例:

  1. 注解在接口上实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //#{name},中的name对应实体类User中的属性名
    @Select("select * from user where name = #{name}")
    List<User> getUserByName3(User user);

    //方法存在多个参数,所有的参数前面必须加上@Param注解
    @Select("select * from user where name = #{name}")
    List<User> getUserByName3(@Param("name") String name);


    //该方法,在接口UserMapper中。com.qsdbl.mapper.UserMapper.getUserByName3
  2. 需要在核心配置文件中绑定接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <mappers>
    <!-- 使用注解开发,需要绑定接口-->
    <mapper class="com.qsdbl.mapper.UserMapper"/>
    </mappers>

    <!--注解与mapper配置文件 可以一块使用。(不知为何接口绑定要放在前边)-->
    <mappers>
    <!-- 使用注解开发,需要绑定接口-->
    <mapper class="com.qsdbl.mapper.UserMapper"/>
    <!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
    <mapper resource="com/qsdbl/mapper/UserMapper.xml"/>
    </mappers>
  3. 测试。使用是一样的,不一样的地方在于接口上使用注解设置sql语句,核心配置文件中绑定接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Test
    public void getUserByName3(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserByName3(new User("小黑"));//参数为User实例
    //List<User> userList = mapper.getUserByName3("小黑");//参数为字符串name

    System.out.println("查询名字为'小黑'的用户信息:");
    for (User user:userList) {
    System.out.println(user);
    }

    sqlSession.close();
    }

Lombok

以前的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生。

依赖:

1
2
3
4
5
6
7
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>

常用的注解:

1
2
3
4
5
6
@Data	在类上使用,生成无参构造器、get、set、toString、hashcode、equals等
@AllArgsConstructor 在类上使用,生成有参构造器(全部)
@NoArgsConstructor 在类上使用,生成无参构造器
@EqualsAndHashCode 在类上使用,生成(重写)equals和hashcode方法
@Getter and @Setter 可以在类或属性上使用
@ToString 在类上使用

更多注解可以参考官网,如何使用Lombok可以参考这篇博客

IDEA中想要实时看到效果,想要安装IDEA插件“Lombok”(首选项-插件-搜索“Lombok”)。

复杂查询

测试环境搭建

  1. 导入Lombok
  2. 新建实体类
  3. 建立(实体类)对应的Mapper接口
  4. 建立Mapper.xml文件(若放在resources文件夹下,记得建立多级文件夹与包名保持一致)
  5. 在核心配置文件中绑定注册我们的Mapper接口或者Mapper配置文件(映射器,使用package标签可同时扫描接口和Mapper配置文件)
  6. 测试一下

多表查询

高级查询,更多介绍查看这篇博客

多对一

多个学生对应一个老师

比如:一个班级中的多个学生班主任只有一个。对于学生而言,就是多对一的关系。

重要知识点:MyBatis结果集映射中的–association标签

查询嵌套

MyBatis处理查询嵌套。类似mysql中的子查询(先查询某张表,其结果作为条件再去查询其他表)

1
2
3
子查询例子:
查询学生小红的班主任名字。先查询学生表的外键tid,再根据tid到老师表中去查询老师的姓名。
select name from teacher where id in (select tid from student where name = '小红')
准备

创建两个数据表:teacher表、student表。student表中的tid字段作为外键关联teacher表的字段id。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
INSERT INTO teacher(`id`, `name`) VALUES (2, '沈老师');

CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '2');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '2');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
需求

查询所有的学生信息,以及对应的老师的信息。

思路
  1. 从student表中查询所有的学生信息

  2. 根据查询出来的学生的tid,在teacher表中查询对应的老师信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SQL语句如下(这个是联表查询,见后边的应用):
    select s.id,s.name,t.name from student s,teacher t where s.tid = t.id;

    查询结果如下,符合要求:
    s.id, s.name, t.name
    1, 小明, 秦老师
    2, 小红, 沈老师
    3, 小张, 秦老师
    4, 小李, 沈老师
    5, 小王, 秦老师
  3. 根据上边的SQL语句可以查询到想要的数据,但是不能在MyBatis中直接使用。要想在MyBatis中实现上述需求,需要用到结果集映射

代码实现

Mapper配置文件解释:

  • ==1、==从student表中查询所有的学生信息

    • ==2、==使用结果集映射,去查询老师信息
    • sql语句中,使用结果集映射resultMap="student-teacher"
    1
    2
    3
    <select id="getStudent" resultMap="student-teacher">
    select * from student
    </select>
  • ==4、==根据查询出来的学生的tid,在teacher表中查询对应的老师信息

    1
    2
    3
    4
    <select id="getTeacher" resultType="Teacher">
    select * from teacher where id = #{tid}
    <!--结果集映射中的select="getTeacher"调用这里的SQL,数据由column="tid"传递过来,这里接收数据使用#{tid}。由于只有一个数据传递过来,所以#{tid}中的tid可以改成任意的名字但是建议使用tid便于理解-->
    </select>
  • ==3、==使用结果集映射,将上边两个SQL整合在一起

    • 该结果集映射,id设为student-teacher,在上边查询学生信息中使用
    • 该结果集映射,返回的数据类型为Student实例(注意我们的需求)。
    • <association select="getTeacher" column="tid" property="teacher" javaType="Teacher"/>
      • select="getTeacher" - 使用上边查询老师信息的select语句。(tid作为参数传递过去)
      • column="tid" property="teacher" - student数据表中的列名tid 与 实体类Student中的属性teacher进行映射(tid作为参数传递过去)
      • property="teacher" javaType="Teacher" - 实体类Student中的属性teacher,数据类型为Teacher(全限定名起的别名为Teacher。“如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。“,也就是说这里的javaType="Teacher"可以省略不写。)
    • 实体类Student的属性teacher要进行结果集映射,其为引用数据类型(对象),所以使用association标签。javaType=""指定属性的类型。
    1
    2
    3
    4
    <resultMap id="student-teacher" type="Student">
    <!-- 复杂的属性,我们需要单独处理。对象:association 集合:collection -->
    <association select="getTeacher" column="tid" property="teacher" javaType="Teacher"/>
    </resultMap>

代码实现(注意Student实体类的属性teacher):

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
73
74
75
76
77
//实体类Student:
package com.qsdbl.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data//在类上使用,生成无参构造器、get、set、toString、hashcode、equals等
@AllArgsConstructor//在类上使用,生成有参构造器(全部)
@NoArgsConstructor//在类上使用,生成无参构造器
public class Student {
private int id;
private String name;
//学生需要关联一个老师(这里不是使用student表中的字段tid,而是使用tid关联的teacher表的实体类实例)
private Teacher teacher;
}

//实体类Teacher:
package com.qsdbl.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data//在类上使用,生成无参构造器、get、set、toString、hashcode、equals等
@AllArgsConstructor//在类上使用,生成有参构造器(全部)
@NoArgsConstructor//在类上使用,生成无参构造器
public class Teacher {
private int id;
private String name;
}



//1、StudentMapper接口,新增一个方法:
List<Student> getStudent();//查询所有的学生信息,以及对应的老师的信息

//2、Mapper配置文件中添加如下sql语句(注意要在mybatis核心配置文件注册该文件):
<select id="getStudent" resultMap="student-teacher">
select * from student
</select>
<resultMap id="student-teacher" type="Student">
<!-- 复杂的属性,我们需要单独处理。对象:association 集合:collection -->
<association select="getTeacher" javaType="Teacher" property="teacher" column="tid"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>

//3、核心配置文件,起别名、注册Mapper配置文件
<!-- 可以给实体类 起别名。包扫描-->
<typeAliases>
<package name="com.qsdbl.bean"/>
</typeAliases>
<!-- 映射器。包扫描的方式,自动注册映射器,注意接口与Mapper配置文件要同名-->
<mappers>
<package name="com.qsdbl.mapper"/>
</mappers>

//4、测试类中进行如下测试:
@Test
public void getStudent(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent();
for (Student student:studentList) {
System.out.println(student);
}

sqlSession.close();
}

//5、测试结果:
Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=2, name=沈老师))
Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=2, name=沈老师))
Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))

Process finished with exit code 0

结果嵌套

MyBatis处理结果嵌套。类似mysql中的联表查询(查询多张表,结果嵌套。多表联合查询)

1
2
3
4
5
联表查询例子(上边的需求那里展示的SQL就是这个):
select student.id,student.name,teacher.name from student,teacher where student.tid = teacher.id;

给表student、teacher取别名(取别名时as可以省略不写):
select s.id as sid,s.name as sname,t.name as tname from student as s,teacher as t where s.tid = t.id;

代码实现:

mapper配置文件解释:

  • 直接使用联表查询的SQL语句,查询出想要的数据再使用结果集映射(resultMap="student_teacher2")对数据进行处理

    1
    2
    3
    <select id="getStudent2" resultMap="student_teacher2">
    select student.id as sid,student.name as sname,teacher.name as tname from student,teacher where student.tid = teacher.id
    </select>
  • 结果集映射student_teacher2,对数据进行处理

    • 返回的数据类型是Student类型,跟上边的一样
    • 实体类Student的属性teacher要进行结果集映射,其为引用数据类型(对象),所以使用association标签。javaType=""指定属性的类型。
    • 查询出来的数据,列sid映射到实体类Student的id属性
    • 查询出来的数据,列sname映射到实体类Student的name属性
  • 查询出来的数据,列tname映射到实体类Student的属性teacher对象中的属性name

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <resultMap id="student_teacher2" type="Student">
    <!-- 属性名、字段名不能有"."所以,这里要设置一下别名-->
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
    <result property="name" column="tname"/>
    </association>
    </resultMap>

    <!--拓展:-->
    <association property="teacher" javaType="Teacher">
    <result property="name" column="tname"/>
    </association>
    <!--查询出来的数据,列tname映射到实体类Student的属性teacher对象中的属性name。所以上边的association可以写成下边的result-->
    <result property="teacher.name" column="tname"/>

代码实现:

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
//1、StudentMapper接口,新增一个方法:
List<Student> getStudent2();//演示联表查询

//2、Mapper配置文件中添加如下sql语句(注意要在mybatis核心配置文件注册该文件):
<!-- 类似联表查询-->
<select id="getStudent2" resultMap="student_teacher2">
select student.id as sid,student.name as sname,teacher.name as tname from student,teacher where student.tid = teacher.id
</select>
<resultMap id="student_teacher2" type="Student">
<!-- 属性名、字段名不能有"."所以,这里要设置一下别名-->
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>

//3、测试类中进行如下测试:
@Test
public void getStudent2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent2();
for (Student student:studentList) {
System.out.println(student);
}

sqlSession.close();
}

//4、测试结果:
//由于只查询了student.id、student.name、teacher.name,没有查询老师id所以这里的老师id全部为0
Student(id=1, name=小明, teacher=Teacher(id=0, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=0, name=沈老师))
Student(id=3, name=小张, teacher=Teacher(id=0, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=0, name=沈老师))
Student(id=5, name=小王, teacher=Teacher(id=0, name=秦老师))

Process finished with exit code 0

一对多

比如:一个老师有多个学生。对于老师而言,就是一对多的关系。

重要知识点:MyBatis结果集映射中的–collection标签

结果嵌套

类似mysql中的联表查询(查询多张表,结果嵌套。多表联合查询)

需求:获取指定老师下的所有学生及老师的信息

代码实现:

mapper配置文件解释:

  • 直接使用联表查询的SQL语句,查询出想要的数据再使用结果集映射(resultMap="teacher_student")对数据进行处理

    1
    2
    3
    4
    5
    6
    7
    <!--按照结果嵌套查询。下边的结果集映射中,属性名、字段名不能有".",所以这里要设置一下别名-->
    <select id="getTeacher" resultMap="teacher_student">
    select t.name as tname,t.id as tid,s.name as sname,s.id as sid
    from student as s,teacher as t
    where s.tid = t.id and t.id = #{id}
    </select>
    <!--TeacherMapper接口提供参数id,传递给#{id}-->
  • 结果集映射teacher_student,对数据进行处理

    • 返回的数据类型是Teacher类型(实体类中增加了一个List<Student>集合,保存该老师对应的学生)
  • 实体类Teacher的属性studentList要进行结果集映射,其为List集合,所以使用collection标签。javaType=""指定属性的类型。集合中的泛型信息,使用ofType=""指定。

    • 查询出来的数据,列tid映射到实体类Teacher的id属性
  • 查询出来的数据,列tname映射到实体类Teacher的name属性

    • 查询出来的数据,列sid、列sname映射到List<Student>集合中的实体类Student
    • 列sid映射到实体类Student的id属性
      • 列sname映射到实体类Student的name属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <resultMap id="teacher_student" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!-- 复杂的属性,我们需要单独处理。对象:association 集合:collection
    javaType="" 指定属性的类型
    集合中的泛型信息,我们使用ofType获取-->
    <collection property="studentList" javaType="ArrayList" ofType="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    </collection>
    </resultMap>

代码实现:

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
//实体类Student:
package com.qsdbl.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data//在类上使用,生成无参构造器、get、set、toString、hashcode、equals等
@AllArgsConstructor//在类上使用,生成有参构造器(全部)
@NoArgsConstructor//在类上使用,生成无参构造器
public class Student {
private int id;
private String name;
private String tid;
}

//实体类Teacher:
package com.qsdbl.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data//在类上使用,生成无参构造器、get、set、toString、hashcode、equals等
@AllArgsConstructor//在类上使用,生成有参构造器(全部)
@NoArgsConstructor//在类上使用,生成无参构造器
public class Teacher {
private int id;
private String name;
//一个老师拥有多个学生(增加一个teacher表中没有的字段,用于保存学生信息)
private List<Student> studentList;
}



//1、TeacherMapper接口,新增一个方法,有参数id:
Teacher getTeacher(@Param("id") int id);//获取指定老师下的所有学生及老师的信息

//2、Mapper配置文件中添加如下sql语句(注意要在mybatis核心配置文件注册该文件):
<!-- 按照结果嵌套查询-->
<select id="getTeacher" resultMap="teacher_student">
select t.name as tname,t.id as tid,s.name as sname,s.id as sid
from student as s,teacher as t
where s.tid = t.id and t.id = #{id}
</select>
<resultMap id="teacher_student" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- 复杂的属性,我们需要单独处理。对象:association 集合:collection
javaType="" 指定属性的类型
集合中的泛型信息,我们使用ofType获取-->
<collection property="studentList" javaType="ArrayList" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>

//3、测试类中进行如下测试:
@Test
public void getTeacher(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();

TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);

sqlSession.close();
}

//4、测试结果:
//由于没有查询学生的tid字段所以这里的tid全部为null
Teacher(id=1, name=秦老师, studentList=[Student(id=1, name=小明, tid=null), Student(id=3, name=小张, tid=null), Student(id=5, name=小王, tid=null)])

Process finished with exit code 0

查询嵌套

类似mysql中的子查询(先查询某张表,其结果作为条件再去查询其他表)

Mapper配置文件解释:

  • ==1、==从teacher表中查询所有的老师信息

    • ==2、==使用结果集映射,去查询学生信息
    • sql语句中,使用结果集映射resultMap="teacher_student2"
    1
    2
    3
    <select id="getTeacher2" resultMap="teacher_student2">
    select * from teacher where id = #{id}
    </select>
  • ==4、==根据查询出来的老师的id,在student表中查询对应的学生信息

    1
    2
    3
    4
    5
    <select id="getStudentByTeacherId" resultType="Student">
    select * from student where tid = #{id}
    </select>

    <!--结果集映射中的select="getStudentByTeacherId"调用这里的SQL,数据由column="id"传递过来,这里接收数据使用#{id}。由于只有一个数据传递过来,所以#{id}中的id可以改成任意的名字但是建议使用id便于理解-->
  • ==3、==使用结果集映射,将上边两个SQL整合在一起

    • 该结果集映射,id设为teacher_student2,在上边第一个select中使用
    • 该结果集映射,返回的数据类型为Teacher实例(注意我们的需求)。
    • <collection select="getStudentByTeacherId" column="id" property="studentList" javaType="ArrayList" ofType="Student"/>
      • select="getStudentByTeacherId" - 使用上边查询学生信息的select语句。(id作为参数传递过去)
      • column="id" property="studentList" - teacher数据表中的列名id 与 实体类Teacher中的属性studentList进行映射(id作为参数传递过去)
      • property="studentList" javaType="ArrayList" - 实体类Teacher中的属性studentList,数据类型为ArrayList(List为集合,应该填写他的实现类,这里使用了ArrayList。不过实测List也可以)
      • property="studentList" javaType="ArrayList" ofType="Student" - 实体类Teacher的属性studentList要进行结果集映射,其为List集合,所以使用collection标签。javaType=""指定属性的类型。集合中的泛型信息,使用ofType=""指定。
    1
    2
    3
    4
    5
    <resultMap id="teacher_student2" type="Teacher">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <collection select="getStudentByTeacherId" column="id" property="studentList" javaType="ArrayList" ofType="Student"/>
    </resultMap>

代码实现:

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
//1、TeacherMapper接口,新增一个方法,有参数id:
Teacher getTeacher2(@Param("id") int id);//获取指定老师下的所有学生及老师的信息

//2、Mapper配置文件中添加如下sql语句(注意要在mybatis核心配置文件注册该文件):
<!-- 查询嵌套-->
<select id="getTeacher2" resultMap="teacher_student2">
select * from teacher where id = #{id}
</select>
<resultMap id="teacher_student2" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection select="getStudentByTeacherId" property="studentList" column="id" javaType="ArrayList" ofType="Student"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{tid}
</select>

//3、测试类中进行如下测试:
@Test
public void getTeacher2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();

TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher);

sqlSession.close();
}

//4、测试结果:
Teacher(id=1, name=秦老师, studentList=[Student(id=1, name=小明, tid=1), Student(id=3, name=小张, tid=1), Student(id=5, name=小王, tid=1)])

Process finished with exit code 0

小结

  1. 关联 - association 【多对一】
  2. 集合 - collection 【一对多】
  3. JavaType & ofType
    1. javaType,用来指定实体类中属性的类型
    2. ofType,用来指定映射到集合(例如List)中的实体类类型(泛型中的约束类型)

注意点:

  • 保证SQL的可读性,尽量保证通俗易懂
  • 注意一对多和多对一中,属性名和字段名的问题
  • 如果问题不好排查错误,可以使用日志,建议使用Log4j

面试高频:

  • MyBatis引擎
    • 例如:innoDB底层原理
  • 索引
    • 索引优化

动态SQL

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

利用动态SQL这一特性可以彻底摆脱这种痛苦。

1
2
3
4
5
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
if
choose (when, otherwise)
trim (where, set)
foreach

环境搭建

  1. 使用到的数据表(COMMENT,设置备注)

    1
    2
    3
    4
    5
    6
    7
    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
  2. 导包(Maven依赖)

    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
    <!--    导入依赖-->
    <dependencies>
    <!-- mysql依赖-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
    </dependency>
    <!-- mybatis依赖-->
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
    </dependency>
    <!-- junit依赖-->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    </dependency>
    <!-- 日志,log4j-->
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    <!-- Lombok-->
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
    <scope>provided</scope>
    </dependency>
    </dependencies>
  3. 编写配置文件(在resources文件夹下)

    • 新建MyBatis核心配置文件mybatis-config.xml
    • properties - 引入外部配置文件,起解耦的作用。
    • typeAliases - 起别名,mapper配置文件中就不需要写长长的全限定名。
    • mappers - 包扫描自动注册映射器,就不需要手动给每个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
    33
    <?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>

    <!-- 引入外部配置文件-->
    <properties resource="db.properties"/>

    <!-- 可以给实体类 起别名。包扫描-->
    <typeAliases>
    <package name="com.qsdbl.bean"/>
    </typeAliases>

    <environments default="mydevelopment-mysql">
    <environment id="mydevelopment-mysql">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    </environments>

    <!-- 映射器。包扫描的方式,自动注册映射器-->
    <mappers>
    <package name="com.qsdbl.mapper"/>
    </mappers>

    </configuration>

    再写一个配置文件(在上边第九行引入)db.properties

    1
    2
    3
    4
    5
    #mysql 8.x以上,使用的驱动是“com.mysql.cj.jdbc.Driver”
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://qsdbl.site:3306/mypos?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username=jack
    password=12345Qsdbl--
  4. 编写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
    25
    26
    27
    package com.qsdbl.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;

    //SqlSessionFactory -> SqlSession
    public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
    try {
    //使用MyBatis第一步:获取SqlSessionFactory对象
    String resource = "mybatis-config.xml";//加载resources文件夹下的文件,直接使用文件名即可
    InputStream inputStream = Resources.getResourceAsStream(resource);
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
    public static SqlSession getSqlSession(){
    return sqlSessionFactory.openSession(true);//添加参数true,执行SQL后会自动提交事务
    }
    }

    用于获取SqlSession实例。一般放在utils包下。

  5. 编写实体类(可以放在bean、pojo、entity包下)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package com.qsdbl.bean;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    @Data//在类上使用,生成无参构造器、get、set、toString、hashcode、equals等
    @AllArgsConstructor//在类上使用,生成有参构造器(全部)
    @NoArgsConstructor//在类上使用,生成无参构造器
    public class Blog {
    private int id;
    private String title;
    private String author;
    private java.util.Date createTime;//使用util包下的Date(不要使用sql包下的)
    private int views;
    }

    使用Lombok注解,不需要手写getter、setter、构造器等。

  6. 编写实体类对应的mapper接口mapper.xml配置文件

    写好mapper接口后,在resources文件夹下,创建与包相应的目录结构再创建mapper.xml配置文件。注意还要到核心配置文件中进行注册,不过前边使用<package name="com.qsdbl.mapper"/>包扫描自动注册了不需要手动注册,想要注意两点:1、配置文件与接口要同名。2、resources文件夹下创建的目录要与包对应上

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--mapper配置文件(接口BlogMapper)-->
    <mapper namespace="com.qsdbl.mapper.BlogMapper">

    </mapper>

Where-If

动态SQL:where、if

mapper配置文件解释:

  • select * from blog where 1=1,可以查询出全部的数据。1=1不会影响正常查询,作用是为了方便下边的and条件拼接

  • 使用if标签,添加动态SQL。在test=“”中写判断条件,title为参数map(parameterType="map")中提供的数据。

    1
    2
    3
    <if test="title != null">
    and title = #{title}
    </if>
  • 一定要注意SQL语句拼接是否正确

  • 改进:使用where标签代替where 1=1

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <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
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
//1、BlogMapper接口,新增一个方法,有参数map:
List<Blog> queryBlogIF(Map map);//查询博客

//2、Mapper配置文件中添加如下sql语句(注意要在mybatis核心配置文件注册该文件):
<select id="queryBlogIF" parameterType="map" resultType="blog">
# 1=1,不会影响正常查询,作用是为了方便下边的and条件拼接
select * from blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
//或(使用where标签,推荐):
<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>

//3、测试类中进行如下测试:
@Test
public void queryBlogIF(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();

BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
// map.put("author","小黑");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}

sqlSession.close();
}

//4、测试结果:
//map中不添加参数时,查询出全部的数据:
Blog(id=6cf8aa93ebb24f1b9ddf823ee91adee3, title=mybatis学习笔记, author=码代码的冰果果, createTime=null, views=237)
Blog(id=74c4c14219c44b429f07ab2ba840bad2, title=IDEA使用技巧, author=小黑, createTime=null, views=20)
Blog(id=d23fdf9af4ba45fbaa4912b9eca66695, title=《围城》读后感, author=小红, createTime=null, views=100)
Blog(id=c4dba1db384c4715a0f0003eb8ebc76a, title=四级错题集, author=张三, createTime=null, views=500)

Process finished with exit code 0


//map中添加author属性为“小黑”,查询出了作者为小黑的博客记录:
Blog(id=74c4c14219c44b429f07ab2ba840bad2, title=IDEA使用技巧, author=小黑, createTime=null, views=20)

Process finished with exit code 0

choose

choose (when, otherwise) - 类似java中的switch-case。

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

  • when,相当于java中的case
  • otherwise,相当于java中的default
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--choose-when,只选择一条,所以第二个when标签里不加and(即使title、author都不为null也是选择一条,title先被匹配到所以会被选择到-->
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
author = #{author}
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</where>
</select>

trim

trim (where, set) - where、set的本质是trim标签。

  • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。==where标签替换掉原本SQL语句中的where命令。==
  • set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。==set标签主要用于update语句,替换掉原本SQL语句中的set命令。==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<update id="updateBlog" parameterType="map">
update 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>

SQL片段

将一些可以复用的语句进行封装,提高复用性。

  1. 使用SQL标签抽取公共部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!--将上边的例子中的if部分提取出来封装在SQL标签中。id名取为”if-title-author-views“-->
    <sql id="if-title-author-views">
    <if test="title != null">
    title = #{title},
    </if>
    <if test="author != null">
    author = #{author},
    </if>
    <if test="views != null">
    views = #{views}
    </if>
    </sql>
  2. 在需要的地方使用include标签引用即可

    1
    2
    3
    4
    5
    6
    7
    8
    <update id="updateBlog" parameterType="map">
    update blog
    <set>
    <!--使用include标签引用封装好的SQL片段,refid为要引用的SQL片段的id-->
    <include refid="if-title-author-views"></include>
    </set>
    where id = #{id}
    </update>

注意事项:

  • 最好基于表单来定义SQL片段
  • SQL片段内最好不要存在where标签(越简单越好)

foreach

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

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

1
2
3
select * from blog where (id = 1 or id = 2 or id = 3);
//等价于:(同时查询id为1、2、3的数据)
select * from blog where id in (1 , 2 , 3);

mapper配置文件解释:

  • 使用foreach标签,对参数集合进行遍历,动态增加SQL语句。

    • collection=""中写携带参数的集合,可以是任何可迭代对象(本例子中,由参数map提供)
    • item=""中写从集合中取出的数据的变量引用(集合项
    • index=""中写从集合中取出的数据的索引
    • open=""中写foreach标签动态拼接SQL语句后,在开头添加的字符(SQL语句)
    • close=""中写foreach标签动态拼接SQL语句后,在结尾添加的字符(SQL语句)
    • separator=""中写foreach标签动态拼接SQL语句时使用的分割符
    1
    2
    3
    <foreach collection="ids" item="id" open="(" close=")" separator="or">
    id = #{id}
    </foreach>
  • 注意:where标签,只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。(所以在上边例子中当ids集合为空时,不会拼接出诸如id = null的SQL语句)

  • 一定要注意SQL语句拼接是否正确

代码演示:

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
//1、BlogMapper接口,新增一个方法,有参数map:
List<Blog> queryBlogForeach(Map map);//根据id同时查询多条记录

//2、Mapper配置文件中添加如下sql语句(注意要在mybatis核心配置文件注册该文件):
<!--select * from blog where (id = 1 or id = 2 or id = 3);传递一个参数map,map中提供一个保存id的集合-->
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
//或:(select * from blog where id in (1 , 2 , 3);)
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="id in (" close=")" separator=",">
#{id}
</foreach>
</where>
</select>

//3、测试类中进行如下测试:
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
//存放id的集合
ArrayList ids = new ArrayList();
// ids.add(1);
// ids.add(2);

//保存参数的map
HashMap map = new HashMap();
map.put("ids",ids);

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

sqlSession.close();
}

//4、测试结果:
//ids集合中不添加参数时,查询出全部的数据:
Blog(id=1, title=mybatis学习笔记, author=码代码的冰果果, createTime=Wed Jan 27 10:07:54 CST 2021, views=237)
Blog(id=2, title=IDEA使用技巧, author=小黑, createTime=Wed Jan 27 10:07:55 CST 2021, views=0)
Blog(id=2, title=《三体》读后感, author=小黑, createTime=Wed Jan 27 10:07:55 CST 2021, views=0)
Blog(id=3, title=《围城》读后感, author=小红, createTime=Wed Jan 27 10:07:55 CST 2021, views=100)
Blog(id=4, title=四级错题集, author=张三, createTime=Wed Jan 27 10:07:55 CST 2021, views=500)

Process finished with exit code 0


//ids集合中添加1、2时,查询出的数据:
Blog(id=1, title=mybatis学习笔记, author=码代码的冰果果, createTime=Wed Jan 27 10:07:54 CST 2021, views=237)
Blog(id=2, title=IDEA使用技巧, author=小黑, createTime=Wed Jan 27 10:07:55 CST 2021, views=0)
Blog(id=2, title=《三体》读后感, author=小黑, createTime=Wed Jan 27 10:07:55 CST 2021, views=0)

Process finished with exit code 0

小结

  • if追加多个条件,choose只会追加一个,匹配一个when后自动“break”
    • where-if,可以选择多个SQL语句拼接在后边
    • choose-when,类似Java中的switch-case,只选择一条
  • ==所谓动态SQL,本质还是SQL语句,只是我们可以在SQL层面去执行一个逻辑代码==
  • 动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
  • 建议:先在mysql中写出完整的SQL,再对应的去修改成为我们的动态SQL实现通用即可

缓存

  1. 什么是缓存(cache)?
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
  2. 为什么要使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据(可以使用缓存)

一级缓存

  • 一级缓存也叫本地缓存:SqlSession
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

测试一级缓存

测试

  1. 开启Log4j日志

  2. 测试在一个SqlSession中查询两次相同记录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public void queryUserById(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();//开启SqlSession

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.queryUserById(1);//第一次查询数据
    System.out.println("查询id为1的用户信息:\n"+user1);
    System.out.println("============分隔符============");
    User user2 = mapper.queryUserById(1);//第二次查询数据
    System.out.println("查询id为1的用户信息:\n"+user2);

    System.out.println("user1 == user2:"+(user1 == user2));

    sqlSession.close();//关闭SqlSession
    }
  3. 查看日志输出

缓存失效的情况

缓存失效的情况:

  • 查询不同的东西(例如:两次查询,查询两个用户信息)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Test
    public void queryUserById(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.queryUserById(1);
    System.out.println("查询id为1的用户信息:\n"+user1);
    System.out.println("============分隔符============");
    User user2 = mapper.queryUserById(2);
    System.out.println("查询id为2的用户信息:\n"+user2);

    System.out.println("user1 == user2:"+(user1 == user2));

    sqlSession.close();
    }

  • “增、删、改”操作,可能会改变原来的数据,所以必定会刷新缓存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Test
    public void updateUser(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.queryUserById(1);
    System.out.println("查询id为"+user1.getId()+"的用户信息:\n"+user1);
    user1.setName("马克思");
    mapper.updateUser(user1);
    System.out.println("将其名字改为:"+user1.getName());

    System.out.println("============分隔符============");
    User user2 = mapper.queryUserById(1);
    System.out.println("查询id为"+user2.getId()+"的用户信息:\n"+user2);

    System.out.println("user1 == user2:"+(user1 == user2));
    if (user1 == user2){
    System.out.println("由于mybatis一级缓存的原因,两个user对象相同(地址)");
    }else{
    System.out.println("由于\"增、删、改\"操作会刷新缓存,所以两个user对象不相同(地址)");
    }

    sqlSession.close();
    }

  • 手动清理缓存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Test
    public void queryUserById(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.queryUserById(1);
    System.out.println("查询id为1的用户信息:\n"+user1);
    System.out.println("============分隔符============");
    sqlSession.clearCache();//手动清理缓存
    User user2 = mapper.queryUserById(1);
    System.out.println("查询id为1的用户信息:\n"+user2);

    System.out.println("user1 == user2:"+(user1 == user2));

    sqlSession.close();
    }

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间对应一个二级缓存
  • 工作机制:
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
    • 新的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在自己对应的缓存(mapper)中

步骤

  1. 核心配置文件中开启全局(二级)缓存(默认就是开启的,所以这一步可以省略。也可起到提示的作用)

    1
    2
    <!--显式的开启全局缓存(默认就是开启的)-->
    <setting name="cacheEnabled" value="true"/>
  2. 在要使用二级缓存的mapper配置文件中开启

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--在mapper配置文件中启用二级缓存(只需要一个cache标签即可,不需要其他配置)-->
    <cache/>

    <!--或添加一些自定义配置:-->
    <cache
    eviction="FIFO"
    flushInterval="60000"
    size="512"
    readOnly="true"/>
  3. 测试

    1. mapper配置文件中,设置readOnly="true"

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

      • 可读写的缓存,readOnly=”false”,序列化与反序列化后的对象(实体类需要实现Serializable接口)与原对象不是同一个对象,故地址不同。
      • 只读的缓存,readOnly=”true”,测试中需要返回缓存对象的相同实例,所以要设置为true
      1
      2
      3
      <!--为了测试需要,设置readOnly="true"(默认为false)-->
      <cache
      readOnly="true"/>
    2. 测试代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      @Test
      public void testCache(){
      SqlSession sqlSession = MyBatisUtils.getSqlSession();
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);//UserMapper
      User user1 = mapper.queryUserById(1);
      System.out.println("查询id为1的用户信息:\n"+user1);
      sqlSession.close();
      System.out.println("= = = = 关闭SqlSession -- 一级缓存失效 = = = = ");

      SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
      UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);//UserMapper
      User user2 = mapper2.queryUserById(1);
      System.out.println("查询id为1的用户信息:\n"+user2);
      sqlSession2.close();
      System.out.println("= = = = 关闭SqlSession -- 一级缓存失效 = = = = ");

      System.out.println("user1 == user2:"+(user1 == user2));
      }

      测试结果:

      未开启二级缓存时:

      开启二级缓存后:

小结

  • 只要开启了二级缓存,在同一个mapper下就有效
  • 所有的数据都会先放在一级缓存中
  • 只有当会话提交或者关闭的时候,才会提交到二级缓存中

缓存原理

自定义缓存-Ehcache

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。

  1. 导入依赖

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

    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"?>
    <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>
  3. 在mapper配置文件中指定使用我们的ehcache缓存实现

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

自定义缓存了解即可,一般使用Redis数据库来做缓存。

搭建环境-总结

快速搭建MyBatis开发环境,参考这里

整合

整合Spring点这里查看。

整合SpringBoot点这里查看(PageHelper分页插件)。

若图片不能正常显示,请在浏览器中打开

欢迎关注我的其它发布渠道