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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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三个字段。
![用户表](/2019/05/29/java/springboot/springboot-mybatis/user-table.png)
在代码中创建对应的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;
}
|
创建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 public interface UserDao {
UserEntity getById(Long id);
@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">
<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); }
}
|
在浏览器中进行访问,结果如下所示:
![结果](/2019/05/29/java/springboot/springboot-mybatis/result.png)
返回的是我在数据库中的造的测试数据,说明我们已经成功的使用mybatis操作了数据库
插件开发
实现自定义插件
实现自定义插件我们只需要继承Interceptor并实现对应的方法就可以了
该接口有三个方法,如下:
是插件实现具体业务逻辑的方法
进行插件注册的方法,我们固定写死就可以
用来传入一些自定义属性,如果没有属性的话,可以不用处理。暂时未发现有其他作用。我们可以在实例化插件的时候进行调用
现在我们已经实现了一个自定义插件,需要启用我们的插件。在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
可以支持多个拦截点有关