整合MyBatis框架
1.MyBatis简介
目前Java持久层最为主流的技术已经是MyBatis,它比JPA和Hibernate更为简单易用和灵活。MyBatis是支持定制化SQL、存储过程和高级映射的持久层框架。MyBatis的配置文件包括两大部分:一是基础配置文件,一个是映射文件。
首先,引入关于MyBatis的starter:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
mybatis-spring-boot-starter是由MyBatis社区开发,先来了解MyBatis的配置和基础。
2.MyBatis的配置
MyBatis是一个基于SqlSessionFactory构建的框架,SqlSessionFactory生成SqlSession接口对象,这是MyBatis的核心。MyBatis应用的生命周期中理应只存在一个SqlSessionFactory对象,且使用单例模式(什么是单例模式:https://blog.youkuaiyun.com/qq_38379983/article/details/89893541)。而构建SqlSessionFactory是通过配置类(Configuration)来完成的,我们可以在配置文件application.properties中进行configuration配置。下图是Configuration可以配置的内容:
其中的typeHandler是MyBatis的重要配置之一,在MyBatis写入和读取数据库的过程中对于不同的数据类型(对Java来说是JavaType,对于数据库来说是JdbcType)进行自定义转换。一般来说,typeHandler的使用集中在枚举类型上。
objectFactory一般使用MyBatis默认提供的对象工厂类(DefaultObjectFactory)即可。
下面通过一个例子看MyBatis的使用流程:
首先,在用户类使用MyBatis别名,即Aliases:
/**** imports ****/
// 标明是一个实体类
@Entity(name = "user")
// 定义映射的表
@Table(name = "t_user")
@Alias(value = "user")// MyBatis指定别名
public class User {
// 标明主键
@Id
// 主键策略,递增
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id = null;
// 定义属性和表的映射关系
@Column(name = "user_name")
private String userName = null;
private String note = null;
// 定义转换器
@Convert(converter = SexConverter.class)
private SexEnum sex = null;
/****setter and getter****/
通过注解@Aliases指定别名为"user",同时,属性中有一个枚举,在MyBatis体系中,枚举可以通过typeHandler进行转换。
性别typeHandler:
/**** imports ****/
// 声明JdbcType为整形
@MappedJdbcTypes(JdbcType.INTEGER)
// 声明JavaType为SexEnum
@MappedTypes(value=SexEnum.class)
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {
// 通过列名读取性别
@Override
public SexEnum getNullableResult(ResultSet rs, String col)
throws SQLException {
int sex = rs.getInt(col);
if (sex != 1 && sex != 2) {
return null;
}
return SexEnum.getEnumById(sex);
}
// 通过下标读取性别
@Override
public SexEnum getNullableResult(ResultSet rs, int idx)
throws SQLException {
int sex = rs.getInt(idx);
if (sex != 1 && sex != 2) {
return null;
}
return SexEnum.getEnumById(sex);
}
// 通过存储过程读取性别
@Override
public SexEnum getNullableResult(CallableStatement cs, int idx)
throws SQLException {
int sex = cs.getInt(idx);
if (sex != 1 && sex != 2) {
return null;
}
return SexEnum.getEnumById(sex);
}
// 设置非空性别参数
@Override
public void setNonNullParameter(PreparedStatement ps, int idx,
SexEnum sex, JdbcType jdbcType) throws SQLException {
ps.setInt(idx, sex.getId());
}
}
在MyBatis中对于typeHandler的要求是实现TypeHandler<T>接口,这里直接继承抽象类BaseTypeHandler<T>实现TypeHandler<T>接口。注解@MapperJdbcTypes声明JdbcType为数据库的整型,@MappedTypes声明JavaType为SexEnum,这样MyBatis就可以对对应的数据类型进行转换了。
为了是这个User类能够与数据库的数据对应,还需要提供一个映射文件(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.springboot.chapter5.dao.MyBatisUserDao">
<select id="getUser" parameterType="long" resultType="user">
select id, user_name as userName, sex, note from t_user where id = #{id}
</select>
</mapper>
这里<mapper>元素的namespace属性指定一个接口,后面会提供这个接口。<select>代表一个查询语句,id属性指代这条SQL,parameterType属性配置为long,则表示是一个长整型参数,resultType指定返回值类型,这里使用的是user别名。
为了启用这个映射文件,我们还需要一个接口(不需要实现任何类),它就是<mapper>里的namespace属性定义的MyBatisUserDAO,代码如下:
package com.springboot.chapter5.dao;
import org.springframework.stereotype.Repository;
import com.springboot.chapter5.pojo.User;
/**** imports ****/
@Repository
public interface MyBatisUserDao {
public User getUser(Long id);
}
有了上面的内容,下面开始配置MyBatis,对映射文件、User别名和typeHandler进行配置,在application.properties中:
#MyBatis映射文件配置
mybatis.mapper-locations=classpath:com/springboot/chapter5/mapper/*.xml
#MyBatis扫描别名包,和注解@Alises联用
mybatis.type-aliases-package=com.springboot.chapter5.pojo
#配置typeHandler扫描包
mybatis.type-handlers-package=com.springboot.chapter5.typehandler
#日志配置
#logging.level.root=DEBUG
#logging.level.org.springframework=DEBUG
#logging.level.org.org.mybatis=DEBUG
日志配置为DEBUG级别,是为了更好的观察测试结果,其他的配置已由mybatis-spring-boot-starter对MyBatis启动做了默认配置。
3.Spring Boot整合MyBatis
这里使用最为简单和实用 的@MapperScan进行扫描加载MyBatis的Mapper:
/**** imports ****/
//定义Spring Boot扫描包路径
@SpringBootApplication(scanBasePackages = {"com.springboot.chapter5"})
//定义JPA接口扫描包路径
//@EnableJpaRepositories(basePackages = "com.springboot.chapter5.dao")
//定义实体Bean扫描包路径
//@EntityScan(basePackages = "com.springboot.chapter5.pojo")
@MapperScan(
//指定扫描包
basePackages = "com.springboot.chapter5.*",
//指定SqlSessionFactory,如果sqlSessionTemplate被指定,则作废
sqlSessionFactoryRef = "sqlSessionFactory",
//指定sqlSessionTemplate,将忽略sqlSessionFactory的配置
sqlSessionTemplateRef = "sqlSessionTemplate",
//markerInterface = Class.class,//限定扫描接口,不常用
annotationClass = Mapper.class
)
public class Chapter5Application {
...
}
有一点需要注意:sqlSessionTemplateRef的优先权大于sqlSessionFactoryRef,当两者都配置后,系统优先选择sqlSessionTemplateref。
本节代码已上传github: https://github.com/lizeyang18/SpringBoot-2.x/tree/master/chapter5
学习永不止步,继续加油~