springboot 集成 mybatis

springboot 配置简单,mybatis 也提供了跟springboot集成的工具包,使得mybatis的配置更简单

引入maven jar包

我们springboot的版本的使用最新的2.1.3的版本,具体的jar包配置如下;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>

<dependencies>
<!-- springboot相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis 相关依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 连接数据库的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>

建表、Entity、Dao

在数据库中创建一个测试使用的用户表(user),添加id,name,age三个字段。

用户表

在代码中创建对应的Entity。

1
2
3
4
5
6
7
8
9
10
11
12
package com.smile.entity;

public class UserEntity {

private Long id;

private String name;

private int age;

//....省略掉get,set方法
}

创建Dao接口

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.smile.dao;

import com.smile.entity.UserEntity;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import org.apache.ibatis.annotations.Mapper;


@Repository
@Mapper// 进行实例化,不添加该注解会出现注入失败的情况。可以使用org.mybatis.spring.annotation.MapperScan 代替
public interface UserDao {
/*
mapper中进行实现
*/
UserEntity getById(Long id);

/*
代码中编写sql
*/
@Select("select * from user where id = #{id}")
UserEntity getById2(Long id);

}

注意

  • 我们需要在Dao接口上添加Mapper注解,或者在springboot启动类中配置MapperScan注解。

配置数据源

我们在spring的配置文件中配置数据源,和mapper的位置。需要注意的是驱动类名,跟之前的有些不一样,这个是新的驱动类。

1
2
3
4
5
6
7
8
9
#数据源
spring.datasource.username=root
spring.datasource.password=****
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 指定要扫描的别名的包名
mybatis.type-aliases-package=com.smile
# 用来指定mapper文件的路径
mybatis.mapper-locations=classpath:/mapper/*.xml

在这里,我指定了别名的包名,此时会使用类名首字母小写作为别名,这样,我们就可以在mapper中使用别名,而不是使用类的全限定名,除非使用Alias指定了别名,会使用指定的别名。

创建实现的Mapper xml

我们在上面指定了mapper的位置,所以我们在resources下创建mapper文件夹,并在下面创建UserDaoMapper.xml,文件的名字没有跟接口的名字保持相同,并添加了Mapper后缀。在xml中我们实现接口中的getById方法。文件内容如下:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 对应的接口定义 -->
<mapper namespace="com.smile.dao.UserDao">
<select id="getById" parameterType="Long" resultType="userEntity">
select * from user where id = #{id}
</select>
</mapper>

namespace 对应的是Dao接口的全类限定名

select标签中id对应的是接口中的方法名,parameterType对应的是入参的类型,resultType对应的是返回的类型,此处使用是别名,对应的是com.smile.entity.UserEntity

测试

此时,我们就可以在service中注入dao的实例对应,进行数据库的操作了,为了方便演示代码,我们直接在控制器中注入。

控制器的代码:

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.smile.controller;


import com.smile.dao.UserDao;
import com.smile.entity.UserEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

@RestController
@RequestMapping("user")
public class UserController {

@Autowired
private UserDao userDao;

@RequestMapping("/getById")
public UserEntity getUser(@RequestParam(value = "id",required = false) Long id){
id = Optional.ofNullable(id).orElse(0L);
return userDao.getById(id);
}


}

在浏览器中进行访问,结果如下所示:

结果

返回的是我在数据库中的造的测试数据,说明我们已经成功的使用mybatis操作了数据库

插件开发

实现自定义插件

实现自定义插件我们只需要继承Interceptor并实现对应的方法就可以了

该接口有三个方法,如下:

  • intercept

是插件实现具体业务逻辑的方法

  • plugin

进行插件注册的方法,我们固定写死就可以

  • setProperties

用来传入一些自定义属性,如果没有属性的话,可以不用处理。暂时未发现有其他作用。我们可以在实例化插件的时候进行调用

现在我们已经实现了一个自定义插件,需要启用我们的插件。在springboot中要启用插件只需要添加Component注解就可以了,mybatis会自己寻找并进行启用。

添加要进行拦截的类型

上面我们已经实现了一个自定义插件,但是插件还是不能进行工作。我们需要定义插件拦截时机

Mybatis 提供了四个类供我们进行拦截:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

我们需要在插件的定义上使用Intercepts注解来定义我们要拦截的时机,示例如下:

1
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})

完成的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 前置业务逻辑处理
Object result = invocation.proceed();
// 后置业务逻辑处理
return result;
}

@Override
public Object plugin(Object target) {
System.out.println("==========plugin" + target.getClass());
// 注册自己到插件的调用链中,如果不注册的话,不会调用
return Plugin.wrap(target, this);
}

@Override
public void setProperties(Properties properties) {
// 需要在实例化插件的时候调用该方法,传入对应的属性信息
System.out.println("MyPlugin setProperties");
}
}

这里有一个比较奇怪的逻辑,就是plugin方法会被调用四次。

怀疑是跟Intercepts可以支持多个拦截点有关