概述
MyBatis-Plus(简称MP)是一个基于MyBatis的增强工具库,它简化了与数据库的交互,提供了更多的便利功能,帮助开发者更高效地进行数据库操作。MP提供了许多实用的功能,包括通用CRUD操作、分页查询、条件构造器、逻辑删除、乐观锁、自动填充字段等。
官方网站:https://mybatis.plus/ (他人捐赠)
官方网站:https://mp.baomidou.com/ (原始官网)
搭建步骤
步骤 1:创建一个新的Maven项目
首先,创建一个新的Maven项目作为你的工程。
步骤 2:添加依赖
在项目的pom.xml文件中添加MyBatis-Plus和MySQL的依赖。
<dependencies>
<!-- MyBatis-Plus依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本</version>
</dependency>
<!-- MySQL驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>最新版本</version>
</dependency>
</dependencies>
请确保将最新版本
替换为MyBatis-Plus和MySQL驱动的实际版本号。
步骤 3:配置数据源
在application.properties
或application.yml
中配置数据库连接信息。
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_mysql_username
spring.datasource.password=your_mysql_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
请将your_database_name
、your_mysql_username
和your_mysql_password
替换为实际的数据库名、MySQL用户名和密码。
步骤 4:创建实体类
创建对应数据库表的实体类。例如,我们创建一个名为User
的实体类。
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
// 省略getter和setter方法
}
在这里,我们使用了@TableName
注解指定实体类对应的数据库表名,@TableId
注解指定主键类型为自增。
步骤 5:创建Mapper接口
创建UserMapper
接口,并继承BaseMapper<User>
。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<User> {
}
步骤 6:配置Mapper扫描路径
在application.properties
或application.yml
中配置Mapper的扫描路径。
mybatis-plus.mapper-locations=classpath:mapper/*.xml
步骤 7:创建Mapper XML文件(可选)
在resources
目录下创建mapper
文件夹,并在其中创建UserMapper.xml
文件。在该XML文件中,你可以编写自定义的SQL语句,MyBatis-Plus将会自动识别并与接口方法进行绑定。
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.example.mapper.UserMapper">
<!-- 你可以在这里编写自定义的SQL语句,MyBatis-Plus会自动识别并与接口方法进行绑定 -->
</mapper>
步骤 8:使用MyBatis-Plus进行数据库操作
现在,你已经完成了MyBatis-Plus环境的搭建。你可以通过UserMapper
接口进行数据库的增删改查操作。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserMapper userMapper;
@Autowired
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
// 示例:插入数据
public void insertUser(User user) {
userMapper.insert(user);
}
// 示例:查询数据
public User getUserById(Long id) {
return userMapper.selectById(id);
}
// 示例:更新数据
public void updateUser(User user) {
userMapper.updateById(user);
}
// 示例:删除数据
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
}
这样,你就可以通过UserService
中的方法来进行数据库操作,而无需编写具体的SQL语句。
优点/特性
-
简化开发
MyBatis-Plus封装了许多常用的CRUD操作,通过继承BaseMapper接口,无需编写繁琐的SQL语句,开发者可以更专注于业务逻辑的实现。这极大地简化了开发过程,提高了开发效率。 -
强大的查询功能
MyBatis-Plus提供了丰富的查询条件构造器,支持链式调用,可以轻松地构建复杂的查询条件,包括等值、范围、模糊查询等。
// 示例:查询年龄在25到30岁之间,并且姓名包含"John"的用户
List<User> userList = userMapper.selectList(
new QueryWrapper<User>()
.between("age", 25, 30)
.like("name", "John")
);
- 分页查询
MyBatis-Plus提供了方便的分页查询功能,开发者只需传入页码和每页记录数即可轻松实现分页查询。
// 示例:查询第2页,每页10条记录的用户列表
Page<User> page = new Page<>(2, 10);
IPage<User> userPage = userMapper.selectPage(page, null);
List<User> userList = userPage.getRecords();
- 自动填充
MyBatis-Plus支持自动填充功能,在插入和更新数据时,可以自动填充指定的字段,例如创建时间和更新时间。
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
- 乐观锁支持
MyBatis-Plus支持乐观锁功能,通过在实体类字段上添加@Version
注解,可以实现乐观锁的功能,防止并发修改冲突。
public class User {
private Long id;
private String name;
private Integer age;
@Version
private Integer version; // 乐观锁版本号
// 省略getter和setter方法
}
- 多租户支持
MyBatis-Plus提供了多租户的支持,可以在SQL查询时自动添加租户字段的条件,实现数据隔离。
// 示例:根据租户ID查询用户列表
List<User> userList = userMapper.selectList(
new QueryWrapper<User>().eq("tenant_id", tenantId)
);
- 逻辑删除
MyBatis-Plus支持逻辑删除功能,通过在实体类字段上添加@TableLogic
注解,可以实现逻辑删除的功能。
public class User {
private Long id;
private String name;
private Integer age;
@TableLogic // 逻辑删除标志
private Integer deleted;
// 省略getter和setter方法
}
-
代码生成器
MyBatis-Plus提供了代码生成器,可以根据数据库表自动生成实体类、Mapper接口以及XML文件,极大地提高了开发效率。 -
性能优化
MyBatis-Plus对查询进行了优化,可以缓存SQL查询结果,减少数据库访问次数,提高性能。 -
可扩展性
MyBatis-Plus提供了许多钩子方法和插件机制,允许开发者自定义扩展功能,满足特定的业务需求。
与Mybatis对比
MyBatis | MyBatis-Plus |
---|---|
基于MyBatis,是MyBatis的增强版。 | 基于MyBatis,是MyBatis的增强工具,提供了更多便捷的功能。 |
需要手动编写大部分的SQL语句。 | 封装了常用的CRUD操作,无需手动编写大部分的SQL语句。 |
提供了基本的CRUD操作,如insert、update、delete、select。 | 在基本的CRUD操作之上,增加了更多的操作方法,如Lambda查询、分页查询等。 |
不提供自动填充、乐观锁、逻辑删除等功能。 | 提供了自动填充、乐观锁、逻辑删除等强大功能。 |
不提供代码生成器。 | 提供了代码生成器,可以根据数据库表自动生成实体类、Mapper接口、XML文件。 |
不提供分页查询功能。 | 提供了方便的分页查询功能。 |
不提供多租户支持。 | 提供了多租户支持,可以实现数据隔离。 |
不提供逻辑删除功能。 | 提供了逻辑删除功能,可以实现数据逻辑删除。 |
需要手动编写Mapper接口和XML文件。 | 可以通过继承BaseMapper接口,自动实现部分CRUD操作,简化开发。 |
MyBatis与MyBatis-Plus的代码实现对比:
1. 使用MyBatis进行数据库操作
// UserMapper.java
public interface UserMapper {
User selectUserById(Long id);
void insertUser(User user);
void updateUser(User user);
void deleteUser(Long id);
}
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectUserById" resultType="com.example.entity.User">
SELECT * FROM user WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.example.entity.User">
INSERT INTO user (id, name, age) VALUES (#{id}, #{name}, #{age})
</insert>
<update id="updateUser" parameterType="com.example.entity.User">
UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}
</update>
<delete id="deleteUser" parameterType="Long">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
2. 使用MyBatis-Plus进行数据库操作
// UserMapper.java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<User> {
}
// UserService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserMapper userMapper;
@Autowired
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
// 查询数据
public User getUserById(Long id) {
return userMapper.selectById(id);
}
// 插入数据
public void insertUser(User user) {
userMapper.insert(user);
}
// 更新数据
public void updateUser(User user) {
userMapper.updateById(user);
}
// 删除数据
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
}
通过对比以上代码,我们可以看到使用MyBatis-Plus的代码更加简洁,无需手动编写SQL语句,通过继承BaseMapper
接口,就可以实现大部分的CRUD操作。此外,MyBatis-Plus还提供了一些额外的功能,如自动填充、乐观锁、逻辑删除等,可以进一步简化开发过程,并提供更多便捷的操作方法。
取消启动banner图标
当使用MyBatis-Plus时,默认情况下,它会显示一个启动banner图标,其中包含MyBatis-Plus的Logo和版本信息。
如果你希望取消MyBatis-Plus的启动banner图标显示,可以按照以下步骤进行配置:
方法一:通过配置文件取消
可以通过在application.properties
或application.yml
配置文件中添加如下配置来取消MyBatis-Plus的启动banner图标显示:
mybatis-plus.banner=false
或者在application.yml
中:
mybatis-plus:
banner: false
方法二:通过编程方式取消
我们也可以通过编程方式取消MyBatis-Plus的启动banner图标显示。以下是一个Java代码示例:
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
app.setBannerMode(Banner.Mode.OFF); // 取消启动banner图标的显示
app.run(args);
}
}
在上述代码中,我们通过SpringApplication
类的setBannerMode()
方法将启动banner图标的显示模式设置为Banner.Mode.OFF
,从而取消了启动时的图标显示。
MyBatisPlus的CRUD操作
操作 | 方法 | 描述 |
---|---|---|
插入数据 | insert(entity) | 插入一条数据,如果数据已存在则会抛出异常。 |
插入或更新数据 | insertOrUpdate(entity) | 插入或更新数据,根据实体类的主键判断数据是否存在,存在则更新,不存在则插入。 |
更新数据 | updateById(entity) | 根据主键更新数据,实体类中未设置的字段会被更新为null。 |
更新数据(忽略null值) | update(entity, updateWrapper) | 根据条件更新数据,updateWrapper为条件构造器,可以指定更新的字段和条件。 |
删除数据 | deleteById(id) | 根据主键删除数据。 |
删除数据(条件删除) | delete(deleteWrapper) | 根据条件删除数据,deleteWrapper为条件构造器,可以指定删除的条件。 |
查询数据(根据ID) | selectById(id) | 根据主键查询数据。 |
查询数据(多个ID) | selectBatchIds(ids) | 根据多个主键查询数据。 |
查询数据(所有数据) | selectList(null) | 查询所有数据。 |
查询数据(条件查询) | selectList(queryWrapper) | 根据条件查询数据,queryWrapper为条件构造器,可以指定查询的条件。 |
查询数据(分页查询) | selectPage(page, queryWrapper) | 分页查询数据,page为分页对象,queryWrapper为条件构造器,可以指定查询的条件。 |
查询数据(总记录数) | selectCount(queryWrapper) | 查询满足条件的数据总记录数,queryWrapper为条件构造器,可以指定查询的条件。 |
查询一条数据 | selectOne(queryWrapper) | 查询满足条件的一条数据,如果满足条件的数据有多条,只返回第一条数据。 |
查询数据(Lambda查询) | lambdaQuery().list() | 使用Lambda表达式进行查询,更加直观和简洁。 |
查询数据(自定义SQL) | selectBySql(sql, params) | 执行自定义SQL语句查询数据,params为参数列表。 |
代码实现辅助理解:
// 插入数据
User user = new User();
user.setName("John");
user.setAge(25);
userMapper.insert(user);
// 更新数据
User user = userMapper.selectById(1L);
user.setAge(26);
userMapper.updateById(user);
// 删除数据
userMapper.deleteById(1L);
// 查询数据(根据ID)
User user = userMapper.selectById(1L);
// 查询数据(条件查询)
List<User> userList = userMapper.selectList(
new QueryWrapper<User>()
.ge("age", 25)
.like("name", "John")
);
// 查询数据(分页查询)
Page<User> page = new Page<>(1, 5); // 查询第1页,每页5条记录
IPage<User> userPage = userMapper.selectPage(page,
new QueryWrapper<User>()
.ge("age", 25)
.like("name", "John")
);
List<User> userList = userPage.getRecords();
// 查询数据(总记录数)
int count = userMapper.selectCount(
new QueryWrapper<User>()
.ge("age", 25)
.like("name", "John")
);
// 查询一条数据
User user = userMapper.selectOne(
new QueryWrapper<User>()
.eq("age", 25)
.orderByDesc("create_time")
);
// 使用Lambda查询
List<User> userList = new LambdaQueryChainWrapper<>(userMapper)
.ge(User::getAge, 25)
.like(User::getName, "John")
.list();
// 执行自定义SQL语句查询数据
String sql = "SELECT * FROM user WHERE age >= ? AND name LIKE ?";
List<User> userList = userMapper.selectBySql(sql, 25, "%John%");
分页查询
概述
当使用MyBatis-Plus(MP)进行分页查询时,我们需要使用Page
对象来表示分页信息,并结合QueryWrapper
来构建查询条件。Page
对象用于指定当前页码和每页记录数,而QueryWrapper
用于添加其他查询条件。
实现步骤
假设我们有一个用户实体类User
,对应数据库中的user
表,表结构如下:
CREATE TABLE user (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
age INT,
email VARCHAR(100)
);
首先,我们需要准备好Spring Boot项目,并添加相应的依赖。在pom.xml
中添加MyBatis-Plus和数据库连接相关的依赖:
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
<!-- 数据库连接相关依赖(此处以MySQL为例) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
接下来,我们需要配置数据库连接信息和MyBatis-Plus分页插件。在application.properties
(或application.yml
)中添加以下配置:
配置方式一:
# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/db_example
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis-Plus配置
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.use-generated-keys=true
mybatis-plus.configuration.db-config.limit=100
在这个配置中,我们是直接在配置文件中使用属性进行配置。具体解释如下:
mybatis-plus.configuration.map-underscore-to-camel-case=true
:
这个配置项表示是否将数据库字段的下划线命名转换为Java对象属性的驼峰命名。如果设置为true
,例如数据库字段为user_name
,对应的Java对象属性会变成userName
。mybatis-plus.configuration.use-generated-keys=true
:
这个配置项表示是否使用数据库自动生成的主键值作为Java对象的主键属性。如果设置为true
,在插入一条记录时,MyBatis-Plus会尝试将数据库自动生成的主键值设置到Java对象的主键属性中。mybatis-plus.configuration.db-config.limit=100
:
这个配置项设置数据库查询的默认分页大小。当在分页查询时没有指定具体的分页大小时,MyBatis-Plus会使用这个配置的默认值。这里设置的是默认分页大小为100。
配置方式二:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1 创建MybatisPlusInterceptor拦截器对象
MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
//2 添加分页拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
在这个配置中,我们是通过Java Config方式进行配置。具体解释如下:
- 首先,我们创建了一个
MybatisPlusInterceptor
拦截器对象,它是MyBatis-Plus提供的拦截器,用于添加各种功能的插件。 - 然后,我们通过
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor())
方式,添加了分页拦截器。分页拦截器是MyBatis-Plus提供的插件,用于自动拦截分页查询的请求,并完成分页查询逻辑。
区别:
- 配置方式不同:
- 配置一是在
application.properties
或application.yml
配置文件中直接设置属性值的方式来配置。 - 配置二是通过Java Config方式来配置,即在Java类中使用
@Configuration
注解,并创建Bean对象来配置。
- 配置一是在
- 配置粒度不同:
- 配置一中的属性设置是全局生效的,影响整个项目的MyBatis-Plus行为。
- 配置二中的拦截器配置是针对特定功能的,例如这里只添加了分页拦截器,如果需要其他功能,还可以添加其他的拦截器。
- 可扩展性不同:
- 配置一较为简单,但如果需要更复杂的功能或自定义插件,则可能需要额外的配置和编写自定义插件。
- 配置二提供了更灵活的方式,可以根据实际需求选择不同的拦截器组合,也可以编写自定义的拦截器来实现更复杂的功能。
接下来,创建User
实体类和对应的Mapper接口:
// User实体类
public class User {
private Long id;
private String name;
private Integer age;
private String email;
// Getters and Setters...
}
// UserMapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
List<User> queryUserByPage(@Param("page") Page<User> page, @Param("user") User user);
}
在上述代码中,我们使用@Mapper
注解标记UserMapper
接口,表明它是一个MyBatis的Mapper接口,并继承了BaseMapper<User>
。BaseMapper
是MyBatis-Plus提供的接口,包含了基本的增删改查方法。
接下来,我们创建一个Service层,用于调用Mapper进行分页查询:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> queryUserByPage(int currentPage, int pageSize, String name) {
Page<User> page = new Page<>(currentPage, pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (name != null && !name.isEmpty()) {
queryWrapper.like("name", name);
}
return userMapper.queryUserByPage(page, null);
}
}
在上述代码中,我们创建了一个UserServiceImpl
实现UserService
接口。在queryUserByPage
方法中,我们创建了一个Page
对象,用于指定当前页码和每页记录数。然后,我们创建了一个QueryWrapper
对象,并根据传入的name
参数(如果有)添加了模糊查询条件。
接下来,在Controller层处理请求,将前端传来的分页参数传递给Service层进行分页查询:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users")
public List<User> getUsersByPage(@RequestParam(name = "page", defaultValue = "1") int currentPage,
@RequestParam(name = "size", defaultValue = "10") int pageSize,
@RequestParam(required = false) String name) {
return userService.queryUserByPage(currentPage, pageSize, name);
}
}
在上述代码中,我们使用@RestController
注解标记UserController
类,表明它是一个控制器类。在getUsersByPage
方法中,我们使用@RequestParam
注解标记请求参数,并指定了默认值。
现在,我们已经完成了整个分页查询的实现。当前端发起GET请求到/users
时,Controller接收请求参数(当前页码、每页记录数、查询关键字),然后调用Service层的方法进行分页查询,最终将查询结果返回给前端。
请确保你的数据库连接配置正确,并且数据库中有相应的数据。运行Spring Boot应用程序后,你可以通过类似以下的URL进行分页查询:
http://localhost:8080/users?page=1&size=10&name=John
以上URL将查询名为"John"的用户信息,返回第1页的10条记录。
分页插件配置方式
-
使用Java Config配置分页插件:
在Spring Boot项目中,你可以使用Java Config来配置MyBatis-Plus分页插件。在配置类中,创建一个PaginationInterceptor
实例,并将其注册到MyBatis的SqlSessionFactoryBean
中。示例代码:
@Configuration public class MyBatisConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
在上述配置中,我们创建了一个
PaginationInterceptor
的Bean,并将其加入到MyBatis的配置中。这样,MyBatis-Plus分页插件就会自动生效,无需额外的配置。 -
使用MyBatis配置文件配置分页插件:
如果你使用传统的MyBatis项目,你可以在MyBatis的配置文件中手动配置分页插件。在mybatis-config.xml
中添加以下配置:<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <plugins> <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/> </plugins> </configuration>
在上述配置中,我们使用
<plugin>
标签指定了PaginationInterceptor
的类路径,这样MyBatis-Plus分页插件就会被自动注册。 -
使用分页拦截器配置插件参数:
MyBatis-Plus的分页插件还支持自定义配置。你可以通过PaginationInterceptor
来配置一些参数,例如数据库类型、是否开启合理化分页等。示例代码:
@Configuration public class MyBatisConfig { @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor interceptor = new PaginationInterceptor(); // 设置数据库类型,例如MySQL interceptor.setDbType(DbType.MYSQL); // 设置是否开启合理化分页 interceptor.setOverflow(true); return interceptor; } }
在上述示例中,我们创建了一个
PaginationInterceptor
的Bean,并设置了数据库类型为MySQL,以及开启了合理化分页功能。 -
使用物理分页配置插件参数:
物理分页是一种更高效的分页方式,可以使用数据库的特定语法来执行分页查询。MyBatis-Plus也支持物理分页,你可以通过PaginationInterceptor
配置物理分页参数。示例代码:
@Configuration public class MyBatisConfig { @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor interceptor = new PaginationInterceptor(); // 设置物理分页参数 interceptor.setDialectType("mysql"); return interceptor; } }
在上述示例中,我们创建了一个
PaginationInterceptor
的Bean,并设置了物理分页的数据库类型为MySQL。
无论使用哪种配置方式,配置MyBatis-Plus分页插件都是非常简单的。根据你的项目需求和开发环境,选择合适的方式来配置分页插件即可。分页插件的配置通常只需在项目启动时完成一次,然后插件将自动拦截分页查询的请求。
MyBatisPlus日志
当使用MyBatis-Plus时,我们可以通过配置来开启MyBatis-Plus的日志输出。MyBatis-Plus使用MyBatis的日志功能,因此我们需要同时配置MyBatis和MyBatis-Plus的日志。
步骤一:配置MyBatis的日志级别
首先,我们需要在配置文件中配置MyBatis的日志级别。MyBatis的日志级别分为TRACE、DEBUG、INFO、WARN和ERROR五个级别,级别从低到高依次为TRACE < DEBUG < INFO < WARN < ERROR。配置MyBatis的日志级别为DEBUG或更高级别,可以让我们看到SQL语句和执行结果。
在application.properties
或application.yml
配置文件中添加如下配置:
# 配置MyBatis的日志级别为DEBUG
logging.level.org.mybatis=DEBUG
步骤二:配置日志框架
MyBatis-Plus使用Slf4j作为日志框架,因此我们需要在项目中添加Slf4j的依赖,并配置对应的日志桥接器。
如果你的项目使用Maven,可以在pom.xml
中添加以下依赖:
<!-- Slf4j 日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version> <!-- 使用当前最新版本 -->
</dependency>
<!-- Logback 日志框架 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version> <!-- 使用当前最新版本 -->
</dependency>
步骤三:配置Logback的日志输出格式
接下来,我们需要配置Logback的日志输出格式,以便更清晰地查看MyBatis-Plus的日志信息。
在项目的src/main/resources
目录下创建logback.xml
文件,并添加如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义日志输出的格式 -->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />
<!-- 控制台输出日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- MyBatis-Plus 的日志输出器 -->
<logger name="com.baomidou.mybatisplus.core" level="DEBUG">
<appender-ref ref="CONSOLE" />
</logger>
<!-- 设置根日志级别为DEBUG -->
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
在上述配置中,我们定义了一个名为CONSOLE
的控制台输出日志的Appender,并将日志输出格式设置为我们定义的${LOG_PATTERN}
。然后,我们将MyBatis-Plus的日志级别设置为DEBUG,并将其日志输出到控制台。
DQL编程控制
概述
当使用MyBatis-Plus(MP)进行DQL(Data Query Language)编程控制时,我们主要关注以下方面:
- 实体类的定义:定义Java实体类,用于映射数据库表结构。
- Mapper接口的定义:定义Mapper接口,用于定义数据库的增删改查操作。
- Mapper XML的配置:配置Mapper接口对应的XML文件,实现具体的数据库操作。
- 使用MyBatis-Plus提供的封装方法:使用MyBatis-Plus提供的便捷方法简化数据库操作。
实现步骤
步骤一:实体类的定义
首先,我们需要定义一个Java实体类,用于映射数据库表的结构。实体类的字段应该与数据库表的字段对应,并使用注解来标识表名、字段名等信息。
示例:假设我们有一个名为User
的数据库表,包含id
、username
和age
字段。
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
private Long id;
private String username;
private Integer age;
// 省略 getter 和 setter 方法
}
步骤二:Mapper接口的定义
接下来,我们需要定义一个Mapper接口,用于定义数据库的增删改查操作。这个接口不需要实现,只需要声明对应的方法。
示例:我们定义一个名为UserMapper
的接口,包含查询用户信息和插入用户信息的方法。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
List<User> selectAllUsers();
void insertUser(User user);
}
注意:UserMapper
继承了BaseMapper<User>
,这是MyBatis-Plus提供的通用Mapper接口,通过继承它,我们可以省去很多基本的CRUD操作的实现。
步骤三:Mapper XML的配置
接下来,我们需要为UserMapper
接口编写对应的Mapper XML文件,实现具体的数据库操作。
示例:创建一个名为UserMapper.xml
的XML文件,并放置在resources/mapper
目录下。
<!-- resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- 查询所有用户信息 -->
<select id="selectAllUsers" resultType="com.example.entity.User">
SELECT id, username, age FROM user
</select>
<!-- 插入用户信息 -->
<insert id="insertUser">
INSERT INTO user (username, age) VALUES (#{username}, #{age})
</insert>
</mapper>
步骤四:使用MyBatis-Plus提供的封装方法
在配置完成后,我们可以直接使用MyBatis-Plus提供的封装方法来进行数据库操作,而无需编写额外的SQL语句。
示例:在Service层中使用UserMapper
来查询所有用户信息和插入用户信息。
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getAllUsers() {
return baseMapper.selectAllUsers();
}
@Override
public void addUser(User user) {
baseMapper.insertUser(user);
}
}
在上述示例中,UserServiceImpl
继承了ServiceImpl<UserMapper, User>
,这是MyBatis-Plus提供的通用Service实现类。通过继承它,我们可以直接调用baseMapper
来实现数据库操作。
查询条件
在多条件查询中,我们可以使用不同的组合来灵活构建查询条件,以满足不同的查询需求。以下是多条件查询可能的组合方式的详细讲解:
官方参考文档:https://baomidou.com/pages/10c804/
组合方式 | 描述 | 代码实现 |
---|---|---|
单条件查询 | 只使用一个条件进行查询 | queryWrapper.eq("name", "John"); |
多条件AND查询 | 多个条件之间使用AND逻辑连接进行查询 | queryWrapper.eq("name", "John").eq("age", 25); |
多条件OR查询 | 多个条件之间使用OR逻辑连接进行查询 | queryWrapper.eq("name", "John").or().eq("age", 30); |
多条件嵌套查询 | 多个条件使用括号嵌套进行查询 | queryWrapper.eq("name", "John").and(qw -> qw.eq("age", 25).or().eq("age", 30)); |
条件参数控制 | 根据条件参数动态构建查询条件 | queryWrapper.eq(StringUtils.isNotBlank(name), "name", name).eq(age != null, "age", age); |
Lambda方式查询 | 使用Lambda表达式进行条件查询 | lambdaQueryWrapper.eq(User::getName, "John").eq(User::getAge, 25); |
Query方式查询 | 使用Query对象进行条件查询 | queryWrapper.eq("name", "John").eq("age", 25); |
带排序的查询 | 在查询条件的基础上添加排序规则 | queryWrapper.eq("name", "John").orderByAsc("age"); |
带分组的查询 | 在查询条件的基础上添加分组规则 | queryWrapper.eq("name", "John").groupBy("age"); |
带分页的查询 | 在查询条件的基础上添加分页规则 | queryWrapper.eq("name", "John").page(new Page<>(1, 10)); |
请注意,以上代码实现仅用于示例,具体的使用方式可能会根据实际情况有所不同。多条件查询非常灵活,可以根据不同的业务需求组合不同的条件来获取符合要求的查询结果。
在实际使用中,可以根据具体的业务需求选择适合的查询方式,根据条件的复杂程度和查询结果的需求,灵活组合各种条件查询方式,以满足不同场景的查询需求。
条件查询
当使用MyBatis-Plus(MP)进行DQL(Data Query Language)编程控制中的条件查询时,我们可以通过不同的方式来实现按条件查询。
在下面,我将详细并全面地讲解三种方式的条件查询:按条件查询、Lambda格式按条件查询(完整)、Lambda格式按条件查询(简化)。并结合代码来辅助理解。
我们以查询用户信息为例,假设有一个名为User
的数据库表,包含id
、username
和age
字段。以下是用户的实体类定义:
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
private Long id;
private String username;
private Integer age;
// 省略 getter 和 setter 方法
}
方式一:按条件查询
在MP中,我们可以使用Wrapper
对象来构建查询条件,并将其作为参数传递给相应的查询方法。Wrapper
是MP提供的查询条件构造器,我们可以通过它来设置各种查询条件。
示例:按条件查询年龄在20岁以上的用户信息。
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByAge(int age) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age", age);
return baseMapper.selectList(queryWrapper);
}
}
在上述示例中,我们创建了一个QueryWrapper<User>
对象,并使用ge
方法设置查询条件为年龄大于等于指定的age
值。然后,我们将QueryWrapper
作为参数传递给selectList
方法,完成按条件查询。
方式二:Lambda格式按条件查询(完整)
MP提供了Lambda表达式的方式来更简洁地构建查询条件。使用Lambda表达式,我们可以在编译时进行语法检查,避免了写错字段名或拼写错误的问题。
示例:按条件查询年龄在20岁以上的用户信息,使用Lambda格式进行完整的条件构造。
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByAge(int age) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.ge(User::getAge, age);
return baseMapper.selectList(lambdaQueryWrapper);
}
}
在上述示例中,我们创建了一个LambdaQueryWrapper<User>
对象,并使用ge
方法设置查询条件为年龄大于等于指定的age
值。使用Lambda表达式,我们可以直接引用实体类的字段,并通过方法引用的方式指定要比较的字段。然后,我们将LambdaQueryWrapper
作为参数传递给selectList
方法,完成按条件查询。
方式三:Lambda格式按条件查询(简化)
对于简单的条件查询,我们可以进一步简化Lambda表达式的写法,使代码更加简洁。
示例:按条件查询年龄在20岁以上的用户信息,使用Lambda格式进行简化的条件构造。
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByAge(int age) {
return lambdaQuery().ge(User::getAge, age).list();
}
}
在上述示例中,我们直接使用lambdaQuery()
方法来创建LambdaQueryWrapper<User>
对象,并链式调用ge
方法设置查询条件为年龄大于等于指定的age
值。然后,通过list()
方法完成按条件查询。
完整实现步骤
User
实体类的定义:
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
private Long id;
private String username;
private Integer age;
// 省略 getter 和 setter 方法
}
UserMapper
接口的定义:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
}
UserMapper.xml
的配置:
<!-- resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
</mapper>
- Service层的实现:
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByAge(int age) {
return baseMapper.selectList(lambdaQuery().ge(User::getAge, age));
}
}
组合条件
在MyBatis-Plus(MP)中,组合条件是指在查询时使用多个条件来限制结果集。常见的组合条件包括"并且关系"(and)和"或者关系"(or)。通过组合条件,我们可以更精确地筛选出满足特定条件的数据。
方式一:并且关系(and)
在MP中,使用Wrapper
对象来构建查询条件时,多个条件之间默认是"并且关系"(and),即多个条件都必须同时满足才能查询到数据。
示例:按条件查询用户名为"John"且年龄在20岁以上的用户信息。
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByNameAndAge(String name, int age) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", name).ge("age", age);
return baseMapper.selectList(queryWrapper);
}
}
在上述示例中,我们创建了一个QueryWrapper<User>
对象,并使用eq
方法设置查询条件为用户名等于指定的name
值,同时使用ge
方法设置查询条件为年龄大于等于指定的age
值。这两个条件之间是"并且关系",即查询结果要满足用户名为"John"且年龄在20岁以上的条件。
方式二:或者关系(or)
如果我们需要使用"或者关系"来组合条件,可以使用or
方法来实现。
示例:按条件查询用户名为"John"或者年龄在20岁以上的用户信息。
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByNameOrAge(String name, int age) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", name).or().ge("age", age);
return baseMapper.selectList(queryWrapper);
}
}
在上述示例中,我们创建了一个QueryWrapper<User>
对象,并使用eq
方法设置查询条件为用户名等于指定的name
值。然后,使用or
方法表示接下来的条件是"或者关系"。紧接着,我们使用ge
方法设置查询条件为年龄大于等于指定的age
值。这两个条件之间是"或者关系",即查询结果要满足用户名为"John"或者年龄在20岁以上的条件。
完整实现步骤
User
实体类的定义:
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
private Long id;
private String username;
private Integer age;
// 省略 getter 和 setter 方法
}
UserMapper
接口的定义:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
}
UserMapper.xml
的配置:
<!-- resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
</mapper>
- Service层的实现:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByNameAndAge(String name, int age) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", name).ge("age", age);
return baseMapper.selectList(queryWrapper);
}
@Override
public List<User> getUsersByNameOrAge(String name, int age) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", name).or().ge("age", age);
return baseMapper.selectList(queryWrapper);
}
}
Null的处理
在多条件查询的场景中,有时候条件的值可能为空。在这种情况下,我们需要针对不同的情况进行处理,以确保查询条件正确且不出现错误。
在MyBatis-Plus(MP)中,处理条件值为空的情况有多种方式,我们可以使用Wrapper
对象的方法来构建查询条件,同时根据条件值是否为空来决定是否添加该条件。
方式一:if语句控制条件追加
在这种方式中,我们可以使用if语句来判断条件是否为空,如果不为空,则将该条件追加到查询条件中。
示例代码如下:
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByAgeRange(Integer minAge, Integer maxAge) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
if (minAge != null) {
queryWrapper.gt(User::getAge, minAge);
}
if (maxAge != null) {
queryWrapper.lt(User::getAge, maxAge);
}
return baseMapper.selectList(queryWrapper);
}
}
在上述示例中,我们通过使用if语句判断minAge
和maxAge
是否为空,如果不为空,则使用gt
和lt
方法来设置年龄的大于和小于条件,实现了根据年龄范围来查询用户信息。
方式二:条件参数控制
MyBatis-Plus提供了条件参数控制的方法,我们可以在查询条件中使用条件判断来决定是否使用该条件。
示例代码如下:
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByAgeRange(Integer minAge, Integer maxAge) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.gt(minAge != null, User::getAge, minAge)
.lt(maxAge != null, User::getAge, maxAge);
return baseMapper.selectList(queryWrapper);
}
}
在上述示例中,我们使用了条件参数控制,通过在gt
和lt
方法中传入一个boolean类型的条件判断,当该条件为true时,才会使用对应的查询条件,否则不使用该条件。
查询射影
在MyBatis-Plus中,查询投影指的是从数据库中查询出特定字段,并将其映射到Java模型类或其他数据结构中。查询投影可以实现只查询模型类中部分属性、包含模型类未定义的属性以及进行分组和分页操作。
查询结果包含模型类中部分属性
在查询投影中,如果我们只想查询模型类中的部分属性,可以使用select
方法来指定要查询的字段。
示例代码如下:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersWithPartialAttributes() {
// 方式一
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "name", "age");
// 方式二
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(User::getId,User::getName,User::getAge);
return baseMapper.selectList(queryWrapper);
}
}
在上述示例中,我们使用QueryWrapper
对象并调用select
方法,指定了要查询的字段名为"id"、"name"和"age"。这样,查询结果将只包含模型类中这三个属性的值。
查询结果包含模型类中未定义的属性
有时候,我们希望查询结果中包含模型类中未定义的属性,比如使用聚合函数计算的字段,可以使用select
方法来实现。
示例代码如下:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<Map<String, Object>> getUsersWithAdditionalAttributes() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("count(*) as count, tel");
queryWrapper.groupBy("tel");
return baseMapper.selectMaps(queryWrapper);
}
}
在上述示例中,我们使用QueryWrapper
对象并调用select
方法,指定了要查询的字段为"count() as count"和"tel"。其中,"count() as count"是使用聚合函数计算的字段,这个字段将返回每个电话号码对应的用户数量。我们还调用了groupBy
方法,按照电话号码进行分组。这样,查询结果将包含模型类未定义的属性"count"和"tel",并且按照电话号码分组。
常见问题
对象名与表名不一致
如果表名与实体类的类名不一致,我们可以使用@TableName
注解来显式设置当前类对应的数据库表名称。通过value
属性来指定映射的数据库表名。
示例代码如下:
@Data
@TableName("tbl_user") // 指定实体类对应的数据库表名为tbl_user
public class User {
private Long id;
private String name;
private Integer age;
// 其他属性...
}
在上述示例中,我们使用@TableName
注解,将实体类User
映射到数据库表tbl_user
。
字段与属性名不一致
在这种情况下,表的字段和实体类的属性名不一致,我们可以使用@TableField
属性注解来显式设置当前属性对应的数据库表中的字段名。通过value
属性来指定映射的数据库字段名。
示例代码如下:
@Data
@TableName("tbl_user")
public class User {
private Long id;
@TableField("user_name") // 表中的字段名为user_name
private String name;
private Integer age;
// 其他属性...
}
在上述示例中,我们使用@TableField
注解,将实体类中的name
属性映射到数据库表中的user_name
字段。
属性中存在不存在的字段
有时候,我们希望在实体类中添加一些属性,但这些属性并未在数据库表中定义。在这种情况下,我们可以使用@TableField
注解,通过exist
属性设置属性在数据库表字段中是否存在,默认为true
。
示例代码如下:
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
@TableField(exist = false) // online字段并未在数据库表中定义
private Boolean online;
// 其他属性...
}
在上述示例中,我们使用@TableField
注解,将实体类中的online
属性设置为在数据库表中不存在的字段。
敏感数据不参与查询
MyBatis-Plus允许在查询时限制查询的字段,以避免开放过多字段的查看权限。我们可以使用@TableField
注解,通过select
属性设置该属性是否参与查询,默认为true
。
示例代码如下:
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
private Integer age;
@TableField(select = false) // 密码字段在查询中不参与
private String password;
// 其他属性...
}
在上述示例中,我们使用@TableField
注解,将实体类中的password
属性设置为在查询中不参与。
DML编程控制
Id生成策略
在MyBatis-Plus中,主键生成策略是通过@TableId
注解来指定的。MyBatis-Plus支持多种主键生成策略,可以根据不同的需求选择合适的策略。
以下是几种常见的主键生成策略及对应的解释和代码实现:
自增策略(AUTO)
这是最常见的主键生成策略,数据库自动递增生成主键。在MySQL中,通常使用AUTO_INCREMENT来实现自增。
示例代码:
@Data
public class Log {
@TableId(type = IdType.AUTO)
private Long id;
// 其他属性...
}
手动指定(NONE)
手动指定主键值,需要在插入数据时自行设置主键值。
示例代码:
@Data
public class Order {
@TableId(type = IdType.NONE)
private String orderId; // 手动指定订单号作为主键
// 其他属性...
}
雪花算法(ASSIGN_ID)
使用Twitter的Snowflake算法生成分布式唯一ID,适用于分布式环境。
示例代码:
@Data
public class TakeoutOrder {
@TableId(type = IdType.ASSIGN_ID)
private Long orderId; // 使用雪花算法生成订单号作为主键
// 其他属性...
}
UUID策略(UUID)
使用UUID作为主键,保证全局唯一性。
示例代码:
@Data
public class Relation {
@TableId(type = IdType.UUID)
private String relationId; // 使用UUID作为关系表主键
// 其他属性...
}
自定义主键生成策略(INPUT)
允许开发者自定义主键值,插入数据时需要自行设置主键值。
示例代码:
@Data
public class Custom {
@TableId(type = IdType.INPUT)
private Long customId; // 自定义主键值
// 其他属性...
}
以上列举了几种常见的主键生成策略,根据不同的表应用不同的id生成策略,你可以在实体类中使用不同的@TableId
注解来设置不同的策略。
@Data
public class Log {
@TableId(type = IdType.AUTO)
private Long id;
// 其他属性...
}
@Data
public class Order {
@TableId(type = IdType.NONE)
private String orderId;
// 其他属性...
}
@Data
public class TakeoutOrder {
@TableId(type = IdType.ASSIGN_ID)
private Long orderId;
// 其他属性...
}
@Data
public class Relation {
@TableId(type = IdType.UUID)
private String relationId;
// 其他属性...
}
@Data
public class Custom {
@TableId(type = IdType.INPUT)
private Long customId;
// 其他属性...
}
这样,不同的实体类对应不同的表就可以使用不同的主键生成策略。
全局策略配置
在MyBatis-Plus中,id生成策略的控制可以通过全局策略配置来统一设置,以确保所有的实体类都使用相同的主键生成策略。全局策略配置是在MyBatis-Plus的配置文件中进行设置的,可以通过配置GlobalConfiguration
来实现。
Step 1:创建全局配置类
首先,我们需要创建一个全局配置类,继承GlobalConfiguration
类,并在该类中配置主键生成策略。
@Configuration
public class MybatisPlusConfig extends GlobalConfiguration {
@Override
public IKeyGenerator keyGenerator() {
// 返回自定义的主键生成器
return new CustomKeyGenerator();
}
}
Step 2:自定义主键生成器
接下来,我们需要实现自定义的主键生成器。自定义主键生成器需要实现IKeyGenerator
接口,并重写executeSql()
方法来生成主键。
public class CustomKeyGenerator implements IKeyGenerator {
@Override
public String executeSql(String incrementerName) {
// 在这里实现你的主键生成逻辑,可以使用雪花算法、UUID等方式生成主键
// 这里简单示例为使用UUID生成主键
return UUID.randomUUID().toString().replace("-", "");
}
}
Step 3:配置全局策略
在MyBatis-Plus的配置文件中,将自定义的全局配置类加入到配置中。
@EnableTransactionManagement
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
@Bean
public GlobalConfiguration globalConfiguration() {
GlobalConfiguration conf = new GlobalConfiguration();
// 设置自定义的主键生成器
conf.setKeyGenerator(new CustomKeyGenerator());
return conf;
}
}
Step 4:使用全局策略配置
在以上配置完成后,MyBatis-Plus会自动使用全局策略配置中的主键生成器,来生成实体类中所有使用@TableId
注解的主键。
示例代码:
@Data
@TableName("tbl_log")
public class Log {
@TableId(type = IdType.INPUT)
private Long id; // 使用自定义主键生成器生成主键
private String content;
// 其他属性...
}
在上述示例中,我们使用了@TableId
注解来设置主键,并通过全局策略配置中的自定义主键生成器生成主键值。
使用全局策略配置可以确保所有的实体类都使用相同的主键生成策略,避免了在每个实体类中都重复配置主键生成策略的麻烦。同时,也提高了代码的复用性和可维护性。
在MyBatis-Plus中,我们可以通过全局策略配置来统一设置所有实体类的主键生成策略。通过在Spring Boot的配置文件中配置相关参数,可以灵活地控制主键生成策略。
1. 配置文件中添加全局策略配置
在Spring Boot的配置文件(通常是application.properties或application.yml)中,可以添加以下配置项来设置全局的主键生成策略:
# application.properties
# 设置全局主键生成策略
mybatis-plus.global-config.db-config.id-type=UUID
# 表名前缀
mybatis-plus.global-config.db-config.table-prefix: t_
# application.yml
mybatis-plus:
global-config:
db-config:
id-type: UUID
table-prefix: t_
在上述配置中,我们将全局的主键生成策略设置为UUID,这将影响所有使用MyBatis-Plus的实体类。
2. 全局策略配置类
除了在配置文件中设置全局策略,我们还可以通过编程的方式来配置全局策略。创建一个全局策略配置类,继承com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig
,并重写相应的方法。
示例代码如下:
@Configuration
public class MybatisPlusConfig {
@Bean
public GlobalConfig globalConfig() {
GlobalConfig globalConfig = new GlobalConfig();
DbConfig dbConfig = new GlobalConfig.DbConfig();
// 设置主键生成策略为UUID
dbConfig.setIdType(IdType.UUID);
globalConfig.setDbConfig(dbConfig);
return globalConfig;
}
}
在上述示例中,我们创建了一个MybatisPlusConfig
类,并通过@Bean
注解将GlobalConfig
对象交给Spring容器管理。在globalConfig
方法中,我们设置了主键生成策略为UUID。
3. 实体类使用@TableId注解
无论是通过配置文件设置全局策略,还是通过编程方式设置全局策略,都需要在实体类中使用@TableId
注解来指定主键生成策略。
示例代码如下:
@Data
public class User {
@TableId(type = IdType.AUTO) // 设置主键生成策略为自增
private Long id;
// 其他属性...
}
在上述示例中,我们使用@TableId
注解,将实体类中的id
属性的主键生成策略设置为自增,而不受全局策略的影响。
4. 多种主键生成策略的混合使用
在实际开发中,不同的表可能需要采用不同的主键生成策略。如果某个表需要使用特定的主键生成策略,可以在实体类中直接指定,而不受全局策略的影响。
示例代码如下:
@Data
public class Order {
@TableId(type = IdType.NONE) // 手动指定订单号作为主键
private String orderId;
// 其他属性...
}
在上述示例中,我们使用@TableId
注解,将实体类中的orderId
属性设置为手动指定主键,而不使用全局策略。
批量操作
多记录删除(批量Delete)
MyBatis-Plus中,我们可以使用deleteBatchIds
方法来实现多记录删除。该方法接收一个ID列表作为参数,可以删除多个记录。
代码实现:
假设我们有一个User
实体类,对应数据库中的user
表。现在我们要删除多个用户,可以使用deleteBatchIds
方法来实现:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public boolean deleteUsersByIds(List<Long> userIds) {
return removeByIds(userIds);
}
}
在上述示例中,我们在UserServiceImpl
类中定义了一个deleteUsersByIds
方法,接收一个userIds
列表作为参数,并调用removeByIds
方法来执行多记录删除操作。
多记录查询(批量Select)
MyBatis-Plus提供了selectBatchIds
方法来实现多记录查询。该方法接收一个ID列表作为参数,可以查询多个记录。
代码实现:
假设我们还是使用上面的User
实体类和user
表,现在我们要根据多个用户ID查询用户信息:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> getUsersByIds(List<Long> userIds) {
return listByIds(userIds);
}
}
在上述示例中,我们在UserServiceImpl
类中定义了一个getUsersByIds
方法,接收一个userIds
列表作为参数,并调用listByIds
方法来执行多记录查询操作。
多记录更新(批量Update)
MyBatis-Plus提供了updateBatchById
方法来实现多记录更新。该方法接收一个实体对象列表作为参数,可以更新多个记录。
代码实现:
假设我们还是使用上面的User
实体类和user
表,现在我们要更新多个用户的信息:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public boolean updateUsers(List<User> users) {
return updateBatchById(users);
}
}
在上述示例中,我们在UserServiceImpl
类中定义了一个updateUsers
方法,接收一个users
列表作为参数,并调用updateBatchById
方法来执行多记录更新操作。
删除操作
删除方式
在实际环境中,删除数据时有两种常见的处理方式:物理删除和逻辑删除。
物理删除
物理删除是指直接从数据库中永久删除数据,使其不再存在于数据库中。这意味着一旦数据被物理删除,就无法恢复,数据将永久丢失。在某些情况下,物理删除可能是合适的选择,比如当数据过期、无用或包含敏感信息时。但是,需要慎重考虑物理删除的后果,确保没有重要数据会被误删。
代码实现:
假设我们使用User
实体类和user
表作为示例,下面是物理删除的代码示例:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public boolean deleteUserById(Long userId) {
return removeById(userId);
}
}
在上述代码中,deleteUserById
方法接收一个userId
作为参数,并调用removeById
方法来执行物理删除操作。这样,执行该方法后,数据库中对应的用户数据将被永久删除。
逻辑删除
逻辑删除是指在数据库中保留数据,但通过设置一个额外的字段(通常是状态字段)来标记数据是否可用。当进行删除操作时,不是直接从数据库中删除数据,而是将状态字段设置为标识数据不可用状态。这样,数据仍然保留在数据库中,但在查询时会自动忽略标记为不可用的数据,从逻辑上实现了删除效果。
逻辑删除的好处是可以避免意外删除重要数据,同时保留了删除的记录用于后续查询或数据审计。逻辑删除适用于那些不希望真正删除数据但需要隐藏或标记的场景。
实现步骤
在MyBatis-Plus中,逻辑删除可以通过注解@TableLogic
实现。首先,在实体类的状态字段上添加@TableLogic
注解:
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
@TableLogic(value = "0", delval = "1")
private Integer status; // 状态字段,用于逻辑删除
}
然后,在UserMapper
接口中启用逻辑删除功能:
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
在默认情况下,MyBatis-Plus启用了逻辑删除功能,但需要注意数据库表中的状态字段命名默认为is_deleted
,也可以通过全局配置进行修改。当执行删除操作时,MyBatis-Plus会自动将status
字段更新为逻辑删除的值(通常是0或1),而不是实际从数据库中删除数据。
关闭逻辑删除
小贴士
- 逻辑删除功能默认是启用的,如果你不需要逻辑删除功能,可以在全局配置中禁用它。
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 禁用逻辑删除
interceptor.addInnerInterceptor(new LogicSqlInjector() {
@Override
protected boolean doRemoveSql(Supplier<MetaObject> metaObject) {
return false;
}
});
return interceptor;
}
}
全局开启配置
在Spring Boot中配置MyBatis-Plus的逻辑删除字面值需要在application.yml或application.properties配置文件中添加相应的配置。MyBatis-Plus提供了一组全局配置,其中包括逻辑删除相关的配置项。
1. 添加配置项
在application.yml
或application.properties
中添加MyBatis-Plus的全局配置项,包括逻辑删除字段名、逻辑删除字面值等。
mybatis-plus:
global-config:
db-config:
# 表名前缀,可选配置,如果有表名前缀,需要加上
table-prefix: tbl_
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑删除字面值:未删除为0
logic-not-delete-value: 0
# 逻辑删除字面值:删除为1
logic-delete-value: 1
在上述配置中,我们配置了表名前缀、逻辑删除字段名以及逻辑删除的字面值。其中,logic-delete-field
表示逻辑删除字段名,logic-not-delete-value
表示未删除状态的字面值,logic-delete-value
表示删除状态的字面值。
2. 实体类配置
在实体类中,需要添加与逻辑删除相关的字段,并使用@TableLogic
注解标注该字段。
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
@Data
@TableName(value = "tbl_user")
public class User {
private Long id;
private String username;
// 逻辑删除字段
@TableLogic
private Integer deleted;
}
在上述代码中,我们在User
实体类中添加了一个名为deleted
的整型字段,并使用@TableLogic
注解标注该字段。这样,MyBatis-Plus会自动识别deleted
字段为逻辑删除字段。
3. 数据库表配置
在数据库表中创建对应的User
表,并添加deleted
字段作为逻辑删除字段。
CREATE TABLE tbl_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
-- 添加逻辑删除字段
deleted INT NOT NULL DEFAULT 0
);
4. 使用逻辑删除功能
现在,我们已经配置了逻辑删除字面值和相应的实体类字段。在使用MyBatis-Plus进行删除操作时,MyBatis-Plus会自动根据配置进行逻辑删除。
实现案例:
假设我们有一个UserService
接口和UserServiceImpl
实现类,我们可以在UserServiceImpl
中调用MyBatis-Plus的删除方法来实现逻辑删除。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public boolean deleteUserById(Long userId) {
// 调用MyBatis-Plus的逻辑删除方法
return removeById(userId);
}
}
在上述代码中,我们通过调用removeById
方法实现逻辑删除操作。根据逻辑删除字面值的配置,MyBatis-Plus会将deleted
字段的值更新为1,表示数据已删除。
乐观锁
概述
MyBatis-Plus(简称MP)提供了乐观锁机制,用于解决多线程并发更新数据时的数据一致性问题。乐观锁是一种乐观的并发控制策略,它不会对数据库进行锁定,而是通过版本号(或者叫作时间戳)来实现并发控制。当两个或多个线程尝试同时修改同一条记录时,乐观锁会根据版本号来判断哪个线程的更新会成功,从而保证数据的一致性。
使用乐观锁的原理
- 在数据库表中添加一个用于乐观锁的版本字段,通常是一个整数或时间戳类型。
- 当执行更新操作时,首先会获取当前记录的版本号,并将版本号作为更新条件。
- 如果另一个线程已经修改了数据并更新了版本号,那么当前更新操作会失败,因为版本号条件不匹配。
- 在更新失败后,可以选择重试更新操作或者执行其他逻辑,以保证数据的一致性。
乐观锁实现示例
在MyBatis-Plus中,配置乐观锁拦截器可以实现对乐观锁的支持,使得在进行更新操作时,自动拼装带有版本号的动态SQL语句。乐观锁拦截器会在执行更新操作前进行拦截,并根据实体类中的乐观锁版本号字段,自动拼装带有版本号的更新SQL语句,从而实现乐观锁的功能。
1. 配置乐观锁拦截器
在Spring Boot中,配置乐观锁拦截器需要创建一个配置类,并在其中注册乐观锁拦截器。乐观锁拦截器是MyBatis-Plus提供的OptimisticLockerInterceptor
类。
@Configuration
public class MyBatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
在上述代码中,我们创建了一个配置类MyBatisPlusConfig
,并在其中注册了OptimisticLockerInterceptor
乐观锁拦截器。
2. 实体类配置
在实体类中添加乐观锁版本号字段,并使用@Version
注解标识该字段为乐观锁版本号。
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
@TableName("user")
public class User {
private Long id;
private String username;
private String email;
@Version // 添加乐观锁注解
private Integer version;
}
在上述代码中,我们在User
实体类的version
字段上添加了@Version
注解,表示该字段是乐观锁版本号字段。
3. 动态SQL语句拼装
在进行更新操作时,乐观锁拦截器会自动拼装带有版本号的动态SQL语句。在执行更新操作时,MyBatis-Plus会检测实体类中的乐观锁版本号字段,并自动在更新SQL语句中增加版本号条件,以实现乐观锁的功能。
下面是一个使用乐观锁的示例代码:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public boolean updateUser(User user) {
// 调用MyBatis-Plus的updateById方法进行更新操作
// 在更新操作时,MyBatis-Plus会自动更新版本号
return updateById(user);
}
}
在上述代码中,我们在UserServiceImpl
中调用updateById
方法来进行更新操作。MyBatis-Plus会自动检测User
对象中的版本号字段,并将版本号作为更新条件,实现乐观锁的功能。
4. 测试乐观锁
为了测试乐观锁的效果,我们可以编写一个测试方法来模拟并发更新的情况。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
@Autowired
private UserService userService;
@Test
public void testOptimisticLock() {
// 假设现在有一条id为1的用户记录,version为0
User user = userService.getById(1L);
// 线程1更新用户的信息
user.setUsername("user1-updated");
userService.updateUser(user);
// 线程2同时更新用户的信息
User user2 = userService.getById(1L);
user2.setUsername("user2-updated");
userService.updateUser(user2);
}
}
在上述测试方法中,我们首先获取一条id为1的用户记录,然后模拟两个线程同时对这条记录进行更新操作。由于乐观锁的存在,只有其中一个线程的更新操作会成功,另一个线程的更新会失败。
5. 结果分析
当两个线程同时更新一条记录时,由于乐观锁的作用,其中一个线程的更新操作会成功,而另一个线程的更新会失败。失败的线程可以根据需要进行重试或者执行其他逻辑,以保证数据的一致性和完整性。
代码生成器
MyBatis-Plus(MP)代码生成器是一个强大的工具,可以帮助开发者快速生成与数据库表对应的Java实体类、Mapper接口、Service接口以及Controller等代码,从而大大提高开发效率。下面将详细并全面地讲解MP代码生成器的使用,并附带相应的代码实现来辅助理解。
1. 添加依赖
首先,我们需要在项目的pom.xml
文件中添加MyBatis-Plus和相关数据库驱动的依赖。假设我们使用MySQL数据库,那么添加如下依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>最新版本</version>
</dependency>
请注意将上述依赖中的最新版本
替换为实际的MyBatis-Plus和MySQL版本号。
2. 配置数据源
接下来,在application.properties
或application.yml
中配置数据源信息。
# 数据库连接信息
spring.datasource.url=jdbc:mysql://localhost:3306/db_example
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis-Plus配置
mybatis-plus.configuration.map-underscore-to-camel-case=true
请将上述配置中的db_example
、root
和your_password
分别替换为实际的数据库名、用户名和密码。
3. 配置代码生成器
在使用MP代码生成器之前,我们需要配置代码生成器的参数,包括生成的代码输出路径、生成的代码包路径、作者信息、表名、主键生成策略等。
我们可以通过创建一个代码生成器的配置类来完成配置,示例如下:
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
public class CodeGenerator {
public static void main(String[] args) {
// 1. 创建代码生成器
AutoGenerator generator = new AutoGenerator();
// 2. 全局配置
GlobalConfig globalConfig = new GlobalConfig();
String projectPath = System.getProperty("user.dir"); // 获取项目根目录
globalConfig.setOutputDir(projectPath + "/src/main/java"); // 设置代码输出路径
globalConfig.setAuthor("YourName"); // 设置作者
globalConfig.setOpen(false); // 生成完成后是否打开输出目录
globalConfig.setDateType(DateType.ONLY_DATE); // 设置日期类型为仅日期
globalConfig.setIdType(IdType.AUTO); // 设置主键生成策略为自增
generator.setGlobalConfig(globalConfig);
// 3. 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/db_example"); // 数据库连接URL
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver"); // 数据库驱动
dataSourceConfig.setUsername("root"); // 数据库用户名
dataSourceConfig.setPassword("your_password"); // 数据库密码
generator.setDataSource(dataSourceConfig);
// 4. 包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example"); // 设置生成的代码包路径
generator.setPackageInfo(packageConfig);
// 5. 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("table_name"); // 设置要生成代码的表名,多个表名用逗号分隔
strategyConfig.setControllerMappingHyphenStyle(true); // 设置Controller的映射地址为连字符形式
generator.setStrategy(strategyConfig);
// 6. 执行代码生成
generator.execute();
}
}
在上述代码中,我们配置了代码生成器的全局配置、数据源配置、包配置以及策略配置。请将代码中的YourName
、db_example
和your_password
分别替换为实际的作者名、数据库名和数据库密码。
4. 运行代码生成器
完成配置后,我们只需运行CodeGenerator
类的main
方法,即可生成对应的代码文件。生成的代码文件将输出在指定的包路径下。
新代码生成器
小贴士
- 旧版适用版本:mybatis-plus-generator 3.5.1 以下版本,3.5.1 及以上的请参考新代码生成器
快速生成
FastAutoGenerator.create("url", "username", "password")
.globalConfig(builder -> {
builder.author("baomidou") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://"); // 指定输出目录
})
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
}))
.packageConfig(builder -> {
builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名
.moduleName("system") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_simple") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
交互式生成
FastAutoGenerator.create(DATA_SOURCE_CONFIG)
// 全局配置
.globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")).fileOverride())
// 包配置
.packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?")))
// 策略配置
.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
.controllerBuilder().enableRestStyle().enableHyphenStyle()
.entityBuilder().enableLombok().addTableFills(
new Column("create_time", FieldFill.INSERT)
).build())
/*
模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker
.templateEngine(new BeetlTemplateEngine())
.templateEngine(new FreemarkerTemplateEngine())
*/
.execute();
// 处理 all 情况
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}