Spring Boot 整合 Mybatis
spring-boot-mybatis
开发环境
开发工具: Intellij IDEA 2018.2.6
springboot: 2.0.7.RELEASE
jdk: 1.8.0_192
maven: 3.6.0
mybatis: 3.4.6
mybatis 简介
什么是 MyBatis ?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
What is MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。 使用这个类库中的类, Spring 将会加载必要的 MyBatis 工厂类和 session 类。 这个类库也提供一个简单的方式来注入 MyBatis 数据映射器和 SqlSession 到业务层的 bean 中。 而且它也会处理事务, 翻译 MyBatis 的异常到 Spring 的 DataAccessException 异常(数据访问异常,译者注)中。最终,它并 不会依赖于 MyBatis,Spring 或 MyBatis-Spring 来构建应用程序代码。
mybatis的常用配置
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods ). |
true | false | false (true in ≤3.4.1) |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或者未知属性类型)的行为。NONE : 不做任何反应WARNING : 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN )FAILING : 映射失败 (抛出 SqlSessionException ) |
NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | 任意正整数 | Not Set (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。 | true | false | False |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。 | true | false | True |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量. 大多都为: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言。 | 一个类型别名或完全限定类名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。 (从3.4.5开始) |
一个类型别名或完全限定类名。 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回null 。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) |
true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | Not set |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Not set |
proxyFactory | 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 or above) |
vfsImpl | 指定VFS的实现 | 自定义VFS的实现的类全限定名,以逗号分隔。 | Not set |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters 选项。(从3.4.1开始) |
true | false | true |
configurationFactory | 指定一个提供Configuration 实例的类。 这个被返回的Configuration实例用来加载被反序列化对象的懒加载属性值。 这个类必须包含一个签名方法static Configuration getConfiguration() . (从 3.2.3 版本开始) |
类型别名或者全类名. | Not set |
Mybatis对应的jdbcType数据类型
类型处理器 | Java 类型 | JDBC 类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | 数据库兼容的 BOOLEAN |
ByteTypeHandler | java.lang.Byte, byte | 数据库兼容的 NUMERIC 或 BYTE |
ShortTypeHandler | java.lang.Short, short | 数据库兼容的 NUMERIC 或 SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer, int | 数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler | java.lang.Long, long | 数据库兼容的 NUMERIC 或 LONG INTEGER |
FloatTypeHandler | java.lang.Float, float | 数据库兼容的 NUMERIC 或 FLOAT |
DoubleTypeHandler | java.lang.Double, double | 数据库兼容的 NUMERIC 或 DOUBLE |
BigDecimalTypeHandler | java.math.BigDecimal | 数据库兼容的 NUMERIC 或 DECIMAL |
StringTypeHandler | java.lang.String | CHAR, VARCHAR |
ClobReaderTypeHandler | java.io.Reader | - |
ClobTypeHandler | java.lang.String | CLOB, LONGVARCHAR |
NStringTypeHandler | java.lang.String | NVARCHAR, NCHAR |
NClobTypeHandler | java.lang.String | NCLOB |
BlobInputStreamTypeHandler | java.io.InputStream | - |
ByteArrayTypeHandler | byte[] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB, LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
DateOnlyTypeHandler | java.util.Date | DATE |
TimeOnlyTypeHandler | java.util.Date | TIME |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
SqlDateTypeHandler | java.sql.Date | DATE |
SqlTimeTypeHandler | java.sql.Time | TIME |
ObjectTypeHandler | Any | OTHER 或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称) |
InstantTypeHandler | java.time.Instant | TIMESTAMP |
LocalDateTimeTypeHandler | java.time.LocalDateTime | TIMESTAMP |
LocalDateTypeHandler | java.time.LocalDate | DATE |
LocalTimeTypeHandler | java.time.LocalTime | TIME |
OffsetDateTimeTypeHandler | java.time.OffsetDateTime | TIMESTAMP |
OffsetTimeTypeHandler | java.time.OffsetTime | TIME |
ZonedDateTimeTypeHandler | java.time.ZonedDateTime | TIMESTAMP |
YearTypeHandler | java.time.Year | INTEGER |
MonthTypeHandler | java.time.Month | INTEGER |
YearMonthTypeHandler | java.time.YearMonth | VARCHAR or LONGVARCHAR |
JapaneseDateTypeHandler | java.time.chrono.JapaneseDate | DATE |
搭建项目
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>spring-boot-mybatis</artifactId>
<groupId>com.andy</groupId>
<version>1.0.7.RELEASE</version>
<modelVersion>4.0.0</modelVersion>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Cairo-SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.3.RELEASE</version>
<configuration>
<!--<mainClass>${start-class}</mainClass>-->
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- application.yml
server:
port: 8081
servlet:
context-path: /
# spring-boot 连接 mysql 配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://host:3306/boot?useSSL=false&characterEncoding=utf8&allowMultiQueries=true
username: root
password: root
# mybatis-springBoot 配置
mybatis:
configuration:
# 全局映射器启用缓存
cache-enabled: true
# 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。
aggressive-lazy-loading: false
# 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载
lazy-loading-enabled: false
# 对于批量更新操作缓存SQL以提高性能
default-executor-type: reuse
# 允许返回不同的结果集以达到通用的效果
multiple-result-sets-enabled: true
# 数据库执行超时时间
default-statement-timeout: 25000
# 打印sql语句
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键
use-generated-keys: true
# 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型
jdbc-type-for-null: null
# 使用驼峰命名法转换字段
map-underscore-to-camel-case: true
# 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分 FULL:全部
auto-mapping-behavior: partial
# 是否可以使用列的别名 (取决于驱动的兼容性) default:true
use-column-label: true
# 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。CGLIB|JAVASSIST, default(JAVASSIST)
proxy-factory: CGLIB
# 指定 MyBatis 增加到日志名称的前缀。任何字符串
log-prefix: test
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: com.andy.mapper
executor-type: reuse
- 启动类
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* <p>
*
* @author Leone
* @since 2018-03-02
**/
@MapperScan(basePackages = "com.andy.mybatis.mapper")
@SpringBootApplication
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class, args);
}
}
- UserController.java
import com.andy.mybatis.entity.User;
import com.andy.mybatis.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
*
* @author Leone
* @since 2018-03-02
**/
@RestController
@RequestMapping("/api/user")
public class UserController {
@Resource
private UserService userService;
@PostMapping
public int insert(@RequestBody User user) {
return userService.insert(user);
}
@PostMapping("/batch")
public int insertBatch(@RequestBody List<User> user) {
return userService.insertBatch(user);
}
@DeleteMapping
public int deleteById(@RequestParam Long userId) {
return userService.delete(userId);
}
@DeleteMapping("/batch")
public int deleteByIds(@RequestParam List<Long> userIds) {
return userService.deleteByIds(userIds);
}
@GetMapping("/page")
public List<User> selectList(@RequestParam Integer start, @RequestParam Integer size) {
return userService.page(start, size);
}
@GetMapping("/{userId}")
public User selectById(@PathVariable("userId") Long userId) {
return userService.selectById(userId);
}
@PutMapping
public int update(@RequestBody User user) {
return userService.update(user);
}
@PutMapping("/batch")
public int updateBatch(@RequestBody List<User> users) {
return userService.updateBatch(users);
}
}
- User.java
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* @author Leone
* @since 2018-03-02
**/
public class User implements Serializable {
private Long userId;
private String account;
private String password;
private String description;
private Integer age;
private Date createTime;
private boolean deleted;
public User() {
}
public User(Long userId, String account, String password, String description, Integer age, Date createTime, Boolean deleted) {
this.userId = userId;
this.account = account;
this.password = password;
this.description = description;
this.age = age;
this.createTime = createTime;
this.deleted = deleted;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
- UserMapper.java
import com.andy.mybatis.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* <p>
*
* @author Leone
* @since 2018-03-02
**/
@Mapper
public interface UserMapper {
@Insert("insert into t_user(`account`, `password`, `age`, `description`, `deleted`, `create_time`) values (#{account},#{password},#{age},#{description},#{deleted},#{createTime})")
int insert(User user);
@Insert({"<script>" +
"insert into t_user(`account`, `password`, `age`, `description`, `deleted`, `create_time`) values" +
"<foreach collection=\"users\" item=\"user\" separator=\",\">" +
"(#{user.account}, #{user.password}, #{user.age}, #{user.description}, #{user.deleted}, #{user.createTime}) " +
"</foreach>" +
"</script>"})
int insertBatch(@Param("users") List<User> users);
@Delete("delete from t_user where user_id = #{userId}")
int deleteByUserId(@Param("userId") Long userId);
@Delete("<script>" +
"delete from t_user where user_id in " +
"<foreach item='item' index='index' collection='userIds' open='(' separator=',' close=')'>" +
"#{item}" +
"</foreach>" +
"</script>")
int deleteByUserIds(@Param("userIds") List<Long> userIds);
@Select("select * from t_user where user_id = #{userId}")
User findByUserId(@Param("userId") Long userId);
@Select("select * from t_user limit ${start}, ${size}")
List<User> page(@Param("start") int start, @Param("size") int size);
@Update({"update t_user set account=#{user.account}, password=#{user.password}, age=#{user.age}, description=#{user.description}, create_time=#{user.createTime, jdbcType=TIMESTAMP}, deleted=#{user.deleted} where user_id = #{user.userId}"})
int update(@Param("user") User user);
@Update({"<script>" +
"<foreach collection=\"users\" item=\"item\" separator=\";\">" +
" update t_user set" +
" account=#{item.account}," +
" password=#{item.password}," +
" age=#{item.age}," +
" description=#{item.description}," +
" create_time=#{item.createTime}," +
" deleted=#{item.deleted}" +
" where user_id=#{item.userId}" +
"</foreach>" +
"</script>"})
int updateBatch(@Param("users") List<User> users);
}
- UserService.java
import com.andy.mybatis.entity.User;
import com.andy.mybatis.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
*
* @author Leone
* @since 2018-03-02
**/
@Slf4j
@Service
public class UserService {
@Resource
private UserMapper userMapper;
/**
* 插入
*
* @param user
* @return
*/
public int insert(User user) {
return userMapper.insert(user);
}
/**
* 批量插入
*
* @param user
* @return
*/
public int insertBatch(List<User> user) {
return userMapper.insertBatch(user);
}
/**
* 更新
*
* @param user
* @return
*/
public int update(User user) {
return userMapper.update(user);
}
/**
* 批量更新
*
* @param users
* @return
*/
public int updateBatch(List<User> users) {
return userMapper.updateBatch(users);
}
/**
* 分页
*
* @param start
* @param size
* @return
*/
public List<User> page(Integer start, Integer size) {
return userMapper.page(start, size);
}
/**
* 根据主键删除
*
* @param userId
*/
public int delete(Long userId) {
return userMapper.deleteByUserId(userId);
}
/**
* 批量删除
*
* @param userIds
*/
public int deleteByIds(List<Long> userIds) {
return userMapper.deleteByUserIds(userIds);
}
public User selectById(Long userId) {
return userMapper.findByUserId(userId);
}
}