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.propertiesspring.datasource.url=jdbc:mysql://localhost:3306/mydbspring.datasource.username=rootspring.datasource.password=secretspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2.3 配置MyBatis
在application.properties文件中配置MyBatis相关参数。
# MyBatis configurationmybatis.mapper-locations=classpath:mapper/*.xmlmybatis.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 mapperPUBLIC "-//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;@Servicepublic class UserService {@Autowiredprivate 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 {@Autowiredprivate UserService userService;@GetMapping("/{id}")public User getUserById(@PathVariable Long id) {return userService.getUserById(id);}@GetMappingpublic List<User> getAllUsers() {return userService.getAllUsers();}@PostMappingpublic void createUser(@RequestBody User user) {userService.createUser(user);}@PutMappingpublic 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.propertiespagehelper.helper-dialect=mysqlpagehelper.reasonable=truepagehelper.support-methods-arguments=truepagehelper.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;@Servicepublic class UserService {@Autowiredprivate 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# 数据源1spring.datasource.primary.url=jdbc:mysql://localhost:3306/primarydbspring.datasource.primary.username=rootspring.datasource.primary.password=secretspring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver# 数据源2spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondarydbspring.datasource.secondary.username=rootspring.datasource.secondary.password=secretspring.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;@Servicepublic class DataService {@Autowiredprivate PrimaryUserMapper primaryUserMapper;@Autowiredprivate SecondaryProductMapper secondaryProductMapper;// User methodspublic 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 methodspublic 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 {@Autowiredprivate 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 {@InjectMocksprivate UserMapper userMapper;@Mockprivate SqlSession sqlSession;@BeforeEachpublic void setUp() {MockitoAnnotations.openMocks(this);}@Testpublic 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;@SpringBootTestpublic class UserServiceIntegrationTest {@Autowiredprivate UserService userService;@Testpublic 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的结合,使得我们能够灵活高效地进行数据访问和处理,提升了开发效率和代码质量。
