SpringBoot访问数据库时,我们可以使用以下三种,JdbcTemplate、Spring Boot Data Jpa、mybatis。
JdbcTemplate是Spring自己提供的,但是其操作不方便,甚至有些繁琐,在实际应用中也是很少会使用,所以这里就不过多介绍了,感兴趣的小伙伴可以自行百度了解。
Spring Boot Data Jpa是基于Hibernate框架来实现的,Hibernate是一个全自动的框架,曾经也和mybatis一样流行,不过Hibernate不方便自定义sql,这便使得其无法满足如今的互联网环境,所以也是渐渐没落了,所以这里也是不过多介绍了,感兴趣的小伙伴可以自行百度了解。
mybatis是一个半自动框架,需要开发者自定义sql,这也使得其可以开发出性能很高的应用,所以现在它也已经成为了目前Java持久层最流行的框架,所以我们这个篇章主要介绍Spring Boot中关于mybatis的使用。
Spring Boot整合Mybatis框架
Spring Boot的默认配置中是不包含mybatis框架的,但是mybatis开发社区自己开发了Spring Boot的启动包。
首先,我们在项目中添加mybatis的依赖以及mysql数据库的依赖,如下所示:
<!--mysql数据库依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis依赖-->
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
这样,我们就可以在项目中使用mysql数据库以及mybatis框架了。
mybatis的配置
mybatis框架的配置文件主要包括两大部分,一个是基础配置文件,还有一个就是映射文件。其中映射文件也可以通过注解的方式配置,但是因为注解的功能以及可读性的限制,所以这里我们不采用注解的方式,而是采用xml方式来处理映射文件。
mybatis操作的核心是SqlSession接口对象,它是由SqlSessionFactory对象来创建的,SqlSessionFactory这个对象的作用也就是为了创建SqlSession接口对象,作用单一,所以在mybatis应用的生命周期中应该只存在一个SqlSessionFactory对象,并且一般都会使用单例模式。
构建SqlSessionFactory对象是需要通过配置类Configuration来完成的,可以在Spring Boot的application.properties文件中配置。下面让我们来看看都可以配置哪些内容。
mybatis别名的使用
为了避免在mybatis的映射文件中书写POJO类的全限定名称(很长,不方便),mybatis提出了别名的概念,就是在mybatis的世界中,给某个POJO类取一个别名,这样mybatis就可以根据这个别名来找到对应的POJO类了。
首先我们创建一个User类,并且采用@Alias注解设置其别名为“user”,代码如下:
@Data
@Accessors(chain = true)
@Alias("user")
public class User {
private Long id;
private String userName;
private SexEnum sex;
}
自定义typeHandler
注意,性别这里,我们采用了枚举类型,所以需要对这个属性开发我们自己的typeHandler来完成从数据库到POJO类的转换,代码如下:
@MappedJdbcTypes(value = JdbcType.INTEGER)
@MappedTypes(value = SexEnum.class)
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {
/**
* 设置非空性别参数
*/
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, SexEnum sexEnum, JdbcType jdbcType) throws SQLException {
preparedStatement.setInt(i, sexEnum.getSex());
}
/**
* 根据列名读取性别
*/
@Override
public SexEnum getNullableResult(ResultSet resultSet, String s) throws SQLException {
int sex = resultSet.getInt(s);
return SexEnum.getBySex(sex);
}
/**
* 根据下标读取性别
*/
@Override
public SexEnum getNullableResult(ResultSet resultSet, int i) throws SQLException {
int sex = resultSet.getInt(i);
return SexEnum.getBySex(sex);
}
/**
* 根据存储过程读取性别
*/
@Override
public SexEnum getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return null;
}
}
在mybatis中,对于typeHandler的要求是需要实现TypeHandler接口,而抽象类BaseTypeHandler也是实现了TypeHandler接口,并且还扩展了其他功能,所以这里我们直接继承抽象类BaseTypeHandler即可。
另外,我们用@MappedJdbcTypes(value = JdbcType.INTEGER)注解设置了性别属性在数据库中的类型,@MappedTypes(value = SexEnum.class)注解设置性别属性在POJO类中的类型。这样,它们就可以互相转换了。
配置映射文件
下面我们创建映射文件UserMapper.xml,内容如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zzm.mapper.UserMapper">
<select id="getById" parameterType="long" resultType="user">
select id,user_name as userName,sex from zzm_sp_learn_user_info where id = #{id}
</select>
</mapper>
namespace中对应的com.zzm.mapper.UserMapper接口如下:
@Repository
public interface UserMapper {
User getById(long id);
}
有了上面的内容,我们在application.properties文件中添加以下配置:
#mybatis映射文件位置配置
mybatis.mapper-locations=classpath:mapper/*.xml
#mybatis扫描别名的包,和@Alias注解联合使用
mybatis.type-aliases-package=com.zzm.pojo
#mybatis的typehandler扫描包配置
mybatis.type-handlers-package=com.zzm.typehandler
#日志配置
logging.level.root=DEBUG
logging.level.org.springframework=DEBUG
logging.level.org.org.mybatis=DEBUG
将日志级别设置为DEBUG是为了方便调试。
对于mybatis的配置到这里就可以了,mybatis-spring-boot-starter已经提供了其他的默认配置,所以这里我们只需要修改我们需要的配置即可,这便是Spring Boot的魅力,以最少的配置进行开发。
下面我们一起看看怎么在Spring Boot中整合它。
Spring Boot中整合mybatis
Spring Boot装配mybatis接口主要有以下几种方式:
- MapperFactoryBean 针对于一个接口配置
- MapperScannerConfigurer 扫描装配
- @MapperScan 扫描装配,和@Repository注解配合使用(相比前两种,这个更加简便,所以实际应用中推荐用这个)
- @Mapper
下面让我们依次看一下这几种的使用。
MapperFactoryBean
MapperFactoryBean一次只能向SpringBoot装配一个mybatis接口,所以这里只是简单介绍。用法如下:
@Bean
public MapperFactoryBean<UserMapper> initUserMapper(){
MapperFactoryBean<UserMapper> mapperFactoryBean = new MapperFactoryBean<>();
mapperFactoryBean.setMapperInterface(UserMapper.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}
MapperScannerConfigurer
实际中,我们的mybatis接口不可能只有一个,而这个类可以配置mybatis接口的扫描规则,实现批量装配的效果。用法如下:
@Bean
public MapperScannerConfigurer mapperScannerConfigurerInit(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
mapperScannerConfigurer.setBasePackage("com.zzm.*");
mapperScannerConfigurer.setAnnotationClass(Repository.class);
return mapperScannerConfigurer;
}
如代码所示,我们配置了mybatis接口的装配规则如下:
- 扫描的包路径为“com.zzm.*”;
- 只有被@Repository注解修饰的接口才会被装配;
@MapperScan 扫描装配
@MapperScan 它是MapperScannerConfigurer 的注解版本,因为SpringBoot推荐使用注解开发,所以这里也推荐大家实际应用中使用这个。
用法如下:
@SpringBootApplication
@Slf4j
@MapperScan(basePackages = {"com.zzm.*"}, annotationClass = Repository.class)
public class Application {
......
}
这里配置的扫描规则和MapperScannerConfigurer配置的是一样的。
@Mapper
这个注解和@Repository注解功能都是一样的,都是将mybaits的接口交由Spring管理,但是相比于@Repository注解,@Mapper注解更加方便。
使用时,仅需要用@Mapper注解修饰需要装配的mubaits接口就可以了,不再需要上述的配置,如下所示:
@Mapper
public interface UserMapper {
User getById(long id);
}
@SpringBootApplication
@Slf4j
public class Application {
.......
}
关于@Repository和@Mapper的区别,可参考如下:
@Repository | @Mapper |
---|---|
Spring提供的注解 | mybatis提供的注解 |
需要配置扫描地址 | 不需要配置扫描地址,通过xml里面的namespace里面的接口地址,生成了Bean后注入到Service层中 |
接下来我们创建测试controller和service(这两个很简单,这里就不过多介绍了),访问测试地址,可以看到效果如下:
数据库中的数据如下:
细心的小伙伴可能已经发现,用户信息中的性别属性值,在我们调用接口查询时自动转换成了枚举类型,而这个工作就是我们前面配置的自定义类型转换器SexTypeHandler 实现的。
而此时的mybatis映射文件中也需要配置我们的自定义类型转换器,如下所示:
<?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.zzm.mapper.UserMapper">
<resultMap id="baseMap" type="user">
<result column="id" property="id" javaType="long" jdbcType="BIGINT"/>
<result column="user_name" property="userName" javaType="String" jdbcType="VARCHAR"/>
<result column="sex" property="sex" javaType="sexEnum" jdbcType="SMALLINT" typeHandler="sexTypeHandler"/>
</resultMap>
<select id="getById" parameterType="long" resultMap="baseMap">
select id,user_name,sex from zzm_sp_learn_user_info where id = #{id}
</select>
</mapper>
mybatis插件
这里我们介绍一下mybatis的插件机制。比如我们如果想在保存或者更新用户信息时,想同时更新对应的创建时间、更新时间、创建人、更新人等信息,那需要怎么做呢?
当然,我们可以在代码中或者sql中完成这个工作,但是如果有很多不同类的数据也需要保存这些信息呢?比如学校信息、课程信息、学生信息…等,如果每个类都要在代码中或者sql中完成这个工作,那无疑会造成大量的重复代码。
所以针对于这个场景,我们就可以采用mybatis的插件机制来完成了。
首先我们在用户表中添加更新人和更新时间两个字段,它俩会在用户信息新增或者保存时同步更新。
定义一个mybatis插件
接下来我们定义一个mybatis插件:
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class})
})
@Slf4j
public class MyPlugin implements Interceptor {
Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.warn("插件拦截方法......");
for(Object obj : invocation.getArgs()){
if(obj instanceof User){
User user = (User) obj;
user.setUpdatedBy(123456L);
user.setUpdateTime(new Date());
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
这里我们设置了拦截mybatis的update方法(即数据的保存和更新都会被拦截到),并且将方法的参数也传了进来。
在方法intercept中,我们拦截了user对象,并且将其更新时间和更新人分别赋值,这样,mybatis在保存数据到数据库时,也会将更新时间和更新人同步保存。
注册插件到Spring
定义好了插件,还需要将其注册到Spring容器中,注册代码如下:
@SpringBootApplication
@Slf4j
public class Application {
@Autowired
private SqlSessionFactory sqlSessionFactory;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
log.warn("启动成功......");
}
@PostConstruct
public void initMybatis(){
Interceptor plugin = new MyPlugin();
Properties properties = new Properties();
properties.setProperty("k1","v1");
properties.setProperty("k2","v2");
properties.setProperty("k3","v3");
plugin.setProperties(properties);
sqlSessionFactory.getConfiguration().addInterceptor(plugin);
}
可以看到,我们在注册插件时,还传递了k1,k2,k3三个属性值,这样,我们就可以在插件中使用这三个值了
最后,启动应用,访问测试接口,测试效果如下:
好了,今天就先到这里了,眼过千遍不如手过一遍,赶紧去自己尝试一下吧。。。。拜拜