SprintBoot集成MyBatis
前面学习中,我们使用的都是基于普通Java控制台工程独立引入的MyBatis框架,在实际开发中,MyBatis框架经常需要嵌入Spring生态中使用。MyBatis对SpringBoot进行了深度整合并提供了简单易用的起步依赖,它会自动完成MyBatis核心组件的创建、注册与生命周期管理,我们无需像前面一样手动编写大量配置文件,这篇笔记我们将学习如何在SpringBoot 3.x中集成MyBatis。
注:本片笔记基于SpringBoot 3.5.x进行介绍,由于SpringBoot 2.x已经EOL,而传统Spring工程搭建的方式在新项目中也几乎没人使用了,因此这篇笔记移除了相关的过时内容。
引入Maven依赖
我们首先创建SpringBoot项目,然后在项目的pom.xml中引入MyBatis的起步依赖和单元测试支持包。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.5</version>
<scope>test</scope>
</dependency>
然后我们还需要引入数据库驱动,不过MySQL的数据库驱动版本号在SpringBoot的Dependency Management中有基于兼容矩阵的维护,因此这里我们不必写<version>字段。
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
添加数据源和MyBatis框架配置
引入MyBatis的起步依赖后,框架的SqlSessionFactory等核心组件会自动注册,我们再添加一些application.properties配置即可,这里我们直接给出一套完整可用的配置。
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/netstore?serverTimezone=Asia/Shanghai&connectTimeout=5000&socketTimeout=60000&tcpKeepAlive=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.max-lifetime=900000
spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.keepalive-time=30000
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.gacfox.demo.model
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.configuration.lazy-loading-enabled=true
配置中,我们使用HikariCP连接池并配置了连接池大小为5~20,连接最大生命周期为900秒,超过时间会被回收防止连接老化,60秒没使用的连接会被释放,每30秒检查一次连接是否有效,防止被网关等中间链路上的组件意外断开时应用层没有感知到。
MyBatis框架层面,我们配置了Mapper XML的路径为src/main/resources/mapper,同时对com.gacfox.demo.model包下的实体类自动做类别名,开启小写下划线转驼峰命名,开启控制台打印执行SQL(仅开发环境,生产环境必须关闭),以及嵌套查询延迟加载支持。
到这里MyBatis与Spring的集成其实已配置完成,我们可以直接编写代码了。
使用MyBatis操作数据库
SpringBoot中,MyBatis的Mapper接口需要标注@Mapper注解,它的作用是告诉MyBatis框架这个接口是Mapper接口,需要为它创建动态代理对象并交给Spring容器管理。实际开发中,另一种方式是在SpringBoot的启动类上标注@MapperScan注解并指定Mapper所在的包扫描路径,两种方法都可以注册Mapper,我们任选一种即可。
package com.gacfox.demo.mapper;
import com.gacfox.demo.model.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User selectUserById(Long id);
}
在Service类中,我们直接通过依赖注入Mapper并调用其中的方法查询数据库即可。
package com.gacfox.demo.service.impl;
import com.gacfox.demo.dto.RoleDTO;
import com.gacfox.demo.dto.UserDTO;
import com.gacfox.demo.mapper.UserMapper;
import com.gacfox.demo.model.User;
import com.gacfox.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
@Autowired
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public UserDTO getUserById(Long id) {
User user = userMapper.selectUserById(id);
if (user == null) {
return null;
}
return toUserDTO(user);
}
private UserDTO toUserDTO(User user) {
UserDTO dto = new UserDTO();
dto.setUserId(user.getUserId());
dto.setUsername(user.getUsername());
dto.setEmail(user.getEmail());
if (user.getRoleList() != null) {
dto.setRoleList(user.getRoleList().stream().map(role -> {
RoleDTO roleDTO = new RoleDTO();
roleDTO.setRoleId(role.getRoleId());
roleDTO.setRolename(role.getRolename());
return roleDTO;
}).toList());
}
return dto;
}
}
如果需要开启事务,我们使用Spring内置的事务管理器即可,下面是例子我们使用TransactionTemplate进行编程式的事务管理。
package com.gacfox.demo.service.impl;
import com.gacfox.demo.dto.RoleDTO;
import com.gacfox.demo.dto.UserCreateDTO;
import com.gacfox.demo.dto.UserDTO;
import com.gacfox.demo.mapper.UserMapper;
import com.gacfox.demo.model.User;
import com.gacfox.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
private final TransactionTemplate transactionTemplate;
@Autowired
public UserServiceImpl(UserMapper userMapper, TransactionTemplate transactionTemplate) {
this.userMapper = userMapper;
this.transactionTemplate = transactionTemplate;
}
@Override
public UserDTO getUserById(Long id) {
User user = userMapper.selectUserById(id);
if (user == null) {
return null;
}
return toUserDTO(user);
}
@Override
public UserDTO addUser(UserCreateDTO createDTO) {
return transactionTemplate.execute(status -> {
User u = new User();
u.setUsername(createDTO.getUsername());
u.setEmail(createDTO.getEmail());
u.setPassword(createDTO.getPassword());
userMapper.insertUser(u);
return toUserDTO(u);
});
}
private UserDTO toUserDTO(User user) {
UserDTO dto = new UserDTO();
dto.setUserId(user.getUserId());
dto.setUsername(user.getUsername());
dto.setEmail(user.getEmail());
if (user.getRoleList() != null) {
dto.setRoleList(user.getRoleList().stream().map(role -> {
RoleDTO roleDTO = new RoleDTO();
roleDTO.setRoleId(role.getRoleId());
roleDTO.setRolename(role.getRolename());
return roleDTO;
}).toList());
}
return dto;
}
}
如果你对事务管理有疑惑,可以参考软件工程/Java/Java企业级应用框架/Spring/SpringFramework/SpringTx章节,这里对Spring事务管理器的使用就不赘述了。