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事务管理器的使用就不赘述了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。