Spring Boot作为一个现代Java开发框架,通过其简化的配置和丰富的功能,极大地提升了开发效率。在数据访问层,MyBatis作为一个优秀的ORM框架,因其灵活性和易用性得到了广泛应用。本文将详细介绍如何在Spring Boot项目中集成MyBatis,涵盖基本配置、动态SQL、多数据源、分页、缓存等多个方面,旨在帮助开发者全面掌握Spring Boot与MyBatis的集成方法。
1. Spring Boot与MyBatis简介
1.1 Spring Boot简介
Spring Boot是Spring生态系统中的一部分,通过简化Spring应用程序的搭建和配置,提升了开发效率。Spring Boot提供了一系列开箱即用的默认配置,支持多种第三方库的快速集成。
1.2 MyBatis简介
MyBatis是一个优秀的持久层框架,它通过XML或注解将SQL语句与Java对象进行映射,提供了比JPA更加灵活的数据库操作方式。MyBatis支持动态SQL、缓存和插件等功能,适用于各种复杂的数据访问场景。
2. Spring Boot集成MyBatis的基本配置
2.1 创建Spring Boot项目
首先,使用Spring Initializr或其他方式创建一个Spring Boot项目,并引入必要的依赖。
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Starter MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- Spring Boot Starter DataSource -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.2 配置数据源
在application.properties
文件中配置数据源信息。
# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2.3 配置MyBatis
在application.properties
文件中配置MyBatis相关参数。
# MyBatis configuration
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.model
2.4 创建数据库表
在MySQL数据库中创建一个示例表。
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL
);
2.5 创建实体类
创建一个与数据库表对应的实体类。
package com.example.model;
public class User {
private Long id;
private String name;
private String email;
// Getters and setters
}
2.6 创建Mapper接口和XML映射文件
创建一个Mapper接口,并在mapper
目录下创建对应的XML映射文件。
package com.example.mapper;
import java.util.List;
import com.example.model.User;
import org.apache.ibatis.annotations.*;
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(Long id);
@Select("SELECT * FROM user")
List<User> getAllUsers();
@Insert("INSERT INTO user(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);
@Update("UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}")
void updateUser(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
void deleteUser(Long id);
}
创建XML映射文件UserMapper.xml
:
<!-- mapper/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.example.mapper.UserMapper">
<resultMap id="UserResultMap" type="com.example.model.User">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="email" property="email" />
</resultMap>
<select id="getUserById" resultMap="UserResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
<select id="getAllUsers" resultMap="UserResultMap">
SELECT * FROM user
</select>
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (name, email) VALUES (#{name}, #{email})
</insert>
<update id="updateUser">
UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
2.7 创建Service类
创建一个Service类,用于处理业务逻辑。
package com.example.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.mapper.UserMapper;
import com.example.model.User;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(Long id) {
return userMapper.getUserById(id);
}
public List<User> getAllUsers() {
return userMapper.getAllUsers();
}
public void createUser(User user) {
userMapper.insertUser(user);
}
public void updateUser(User user) {
userMapper.updateUser(user);
}
public void deleteUser(Long id) {
userMapper.deleteUser(id);
}
}
2.8 创建Controller类
创建一个Controller类,用于处理HTTP请求。
package com.example.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.example.model.User;
import com.example.service.UserService;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@PostMapping
public void createUser(@RequestBody User user) {
userService.createUser(user);
}
@PutMapping
public void updateUser(@RequestBody User user) {
userService.updateUser(user);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
3. MyBatis动态SQL
MyBatis支持使用动态SQL来构建灵活的查询语句。动态SQL可以通过XML映射文件中的<if>
、<choose>
、<when>
、<otherwise>
等标签来实现。
3.1 使用动态SQL
在UserMapper.xml
中添加一个动态SQL查询示例。
<!-- mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="UserResultMap" type="com.example.model.User">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="email" property="email" />
</resultMap>
<select id="getUserById" resultMap="UserResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
<select id="getAllUsers" resultMap="UserResultMap">
SELECT * FROM user
</select>
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (name, email) VALUES (#{name}, #{email})
</insert>
<update id="updateUser">
UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser">
DELETE FROM user WHERE id = #{id}
</delete>
<!-- 动态SQL示例 -->
<select id="findUsersByNameAndEmail" resultMap="UserResultMap">
SELECT * FROM user
<where>
<if test="name
!= null">
AND name = #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</select>
</mapper>
3.2 修改Mapper接口
在UserMapper
接口中添加对应的方法。
package com.example.mapper;
import java.util.List;
import com.example.model.User;
import org.apache.ibatis.annotations.*;
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(Long id);
@Select("SELECT * FROM user")
List<User> getAllUsers();
@Insert("INSERT INTO user(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);
@Update("UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}")
void updateUser(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
void deleteUser(Long id);
// 动态SQL方法
List<User> findUsersByNameAndEmail(@Param("name") String name, @Param("email") String email);
}
4. MyBatis分页插件
在实际应用中,分页查询是一个常见的需求。MyBatis支持通过分页插件来实现分页功能。
4.1 添加分页插件依赖
在pom.xml
中添加分页插件的依赖。
<!-- pom.xml -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
4.2 配置分页插件
在application.properties
中配置分页插件参数。
# application.properties
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql
4.3 使用分页插件
在Service类中使用分页插件进行分页查询。
package com.example.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.example.mapper.UserMapper;
import com.example.model.User;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(Long id) {
return userMapper.getUserById(id);
}
public List<User> getAllUsers() {
return userMapper.getAllUsers();
}
public void createUser(User user) {
userMapper.insertUser(user);
}
public void updateUser(User user) {
userMapper.updateUser(user);
}
public void deleteUser(Long id) {
userMapper.deleteUser(id);
}
// 分页查询
public PageInfo<User> getUsersByPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.getAllUsers();
return new PageInfo<>(users);
}
}
5. MyBatis多数据源配置
在一些复杂的应用中,可能需要访问多个数据源。Spring Boot和MyBatis支持多数据源配置,下面我们将介绍如何配置和使用多数据源。
5.1 添加多数据源配置
在application.properties
中配置多个数据源。
# application.properties
# 数据源1
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primarydb
spring.datasource.primary.username=root
spring.datasource.primary.password=secret
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源2
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondarydb
spring.datasource.secondary.username=root
spring.datasource.secondary.password=secret
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
5.2 创建数据源配置类
创建一个数据源配置类,配置多个数据源。
package com.example.config;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.boot.jdbc.DataSourceBuilder;
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean.getObject();
}
@Primary
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
package com.example.config;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.boot.jdbc.DataSourceBuilder;
@Configuration
@MapperScan(basePackages = "com.example.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean.getObject();
}
@Bean(name = "secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
5.3 创建Mapper接口
在com.example.mapper.primary
和com.example.mapper.secondary
包中创建Mapper接口。
package com.example.mapper.primary;
import java.util.List;
import com.example.model.User;
import org.apache.ibatis.annotations.*;
public interface PrimaryUserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User getUserById(Long id);
@Select("SELECT * FROM user")
List<User> getAllUsers();
@Insert("INSERT INTO user(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);
@Update("UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}")
void updateUser(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
void deleteUser(Long id);
}
package com.example.mapper.secondary;
import java.util.List;
import com.example.model.Product;
import org.apache.ibatis.annotations.*;
public interface SecondaryProductMapper {
@Select("SELECT * FROM product WHERE id = #{id}")
Product getProductById(Long id);
@Select("SELECT * FROM product")
List<Product> getAllProducts();
@Insert("INSERT INTO product(name, price) VALUES(#{name}, #{price})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertProduct(Product product);
@Update("UPDATE product SET name = #{name}, price = #{price} WHERE id = #{id}")
void updateProduct(Product product);
@Delete("DELETE FROM product WHERE id = #{id}")
void deleteProduct(Long id);
}
5.4 创建Service类
创建Service类,分别调用不同数据源的Mapper接口。
package com.example.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.mapper.primary.PrimaryUserMapper;
import com.example.mapper.secondary.SecondaryProductMapper;
import com.example.model.User;
import com.example.model.Product;
@Service
public class DataService {
@Autowired
private PrimaryUserMapper primaryUserMapper;
@Autowired
private SecondaryProductMapper secondaryProductMapper;
// User methods
public User getUserById(Long id) {
return primaryUserMapper.getUserById(id);
}
public List<User> getAllUsers() {
return primaryUserMapper.getAllUsers();
}
public void createUser(User user) {
primaryUserMapper.insertUser(user);
}
public void updateUser(User user) {
primaryUserMapper.updateUser(user);
}
public void deleteUser(Long id) {
primaryUserMapper.deleteUser(id);
}
// Product methods
public Product getProductById(Long id) {
return secondaryProductMapper.getProductById(id);
}
public List<Product> getAllProducts() {
return secondaryProductMapper.getAllProducts();
}
public void createProduct(Product product) {
secondaryProductMapper.insertProduct(product);
}
public void updateProduct(Product product) {
secondaryProductMapper.updateProduct(product);
}
public void deleteProduct(Long id) {
secondaryProductMapper.deleteProduct(id);
}
}
5.5 创建Controller类
创建一个Controller类,调用Service方法处理HTTP请求。
package com.example.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.example.model.User;
import com.example.model.Product;
import com.example.service.DataService;
@RestController
@RequestMapping("/data")
public class DataController {
@Autowired
private DataService dataService;
// User endpoints
@GetMapping("/users/{id}")
public User getUserById(@PathVariable Long id) {
return dataService.getUserById(id);
}
@GetMapping("/users")
public List<User> getAllUsers() {
return dataService.getAllUsers();
}
@PostMapping("/users")
public void createUser(@RequestBody User user) {
dataService.createUser(user);
}
@PutMapping("/users")
public void updateUser(@RequestBody User user) {
dataService.updateUser(user);
}
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) {
dataService.deleteUser(id);
}
// Product endpoints
@GetMapping("/products/{id}")
public Product getProductById(@PathVariable Long id) {
return dataService.getProductById(id);
}
@GetMapping("/products")
public List<Product> getAllProducts() {
return dataService.getAllProducts();
}
@PostMapping("/products")
public void createProduct(@RequestBody Product product) {
dataService.createProduct(product);
}
@PutMapping("/products")
public void updateProduct(@RequestBody Product product) {
dataService.updateProduct(product);
}
@DeleteMapping("/products/{id}")
public void deleteProduct(@PathVariable Long id) {
dataService.deleteProduct(id);
}
}
6. MyBatis缓存
MyBatis支持一级缓存和二级缓存,通过配置缓存可以提高数据访问的性能。
6.1 一级缓存
MyBatis的一级缓存是SqlSession级别的缓存,默认情况下是开启的。每个SqlSession在操作过程中会缓存查询结果,直到SqlSession被关闭或清理。
6.2 二级缓存
MyBatis的二级缓存是Mapper级别的缓存,需要在Mapper映射文件中配置。
<!-- mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<cache/>
<resultMap id="UserResultMap" type="com.example.model.User">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="email" property="email" />
</resultMap>
<select id="getUserById" resultMap="UserResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
<select id="getAllUsers" resultMap="UserResultMap">
SELECT * FROM user
</select>
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (name, email) VALUES (#{name}, #{email})
</insert>
<update id="updateUser">
UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
7. 单元测试和集成测试
为了确保MyBatis与Spring Boot集成的代码质量和可靠性,开发者应编写单元测试和集成测试。
7.1 单元测试
使用JUnit和Mockito编写Mapper接口的单元测试。
package com.example.mapper;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.example.model.User;
public class UserMapperTest {
@InjectMocks
private UserMapper userMapper;
@Mock
private SqlSession sqlSession;
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
public void testGetUserById() {
User user = new User();
user.setId(1L);
user.setName("John Doe");
user.setEmail("john.doe@example.com");
when(sqlSession.selectOne("com.example.mapper.UserMapper.getUserById", 1L)).thenReturn(user);
User foundUser = userMapper.getUserById(1L);
assertEquals("John Doe", foundUser.getName());
verify(sqlSession, times(1)).selectOne("com.example.mapper.UserMapper.getUserById", 1L);
}
}
7.2 集成测试
使用Spring Boot的@SpringBootTest
注解编写集成测试。
package com.example.service;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.example.model.User;
@SpringBootTest
public class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Test
public void testCreateUser() {
User user = new User();
user.setName("Jane Doe");
user.setEmail("jane.doe@example.com");
userService.createUser(user);
User createdUser = userService.getUserById(user.getId());
assertNotNull(createdUser);
assertEquals("Jane Doe", createdUser.getName());
}
}
8. 总结
通过本文的介绍,我们详细了解了如何在Spring Boot项目中集成MyBatis,涵盖了基本配置、动态SQL、多数据源、分页、缓存以及单元测试和集成测试等多个方面。Spring Boot与MyBatis的结合,使得我们能够灵活高效地进行数据访问和处理,提升了开发效率和代码质量。