Mybatis-Plus 實戰(zhàn)使用與最佳實踐
引言
在現(xiàn)代Java企業(yè)級開發(fā)中,數(shù)據(jù)持久層框架的選擇直接影響著開發(fā)效率和代碼質(zhì)量。Mybatis作為優(yōu)秀的持久層框架,以其靈活性和可控性贏得了廣大開發(fā)者的青睞。而Mybatis-Plus作為Mybatis的增強工具,在保持Mybatis原有特性的基礎(chǔ)上,提供了更加便捷的CRUD操作、強大的條件構(gòu)造器、代碼生成器等功能,極大地提升了開發(fā)效率。
本文將從實戰(zhàn)角度出發(fā),深入解析Mybatis-Plus的核心功能和最佳實踐,幫助開發(fā)者快速掌握這一強大的開發(fā)工具。我們將通過豐富的代碼示例和實際應(yīng)用場景,全面展示Mybatis-Plus在企業(yè)級項目中的應(yīng)用價值。

第一章:Mybatis-Plus概述與環(huán)境搭建
1.1 Mybatis-Plus簡介與核心特性
Mybatis-Plus(簡稱MP)是一個Mybatis的增強工具,在Mybatis的基礎(chǔ)上只做增強不做改變,為簡化開發(fā)、提高效率而生。它的設(shè)計理念是"只做增強不做改變",這意味著引入Mybatis-Plus不會對現(xiàn)有的Mybatis構(gòu)架產(chǎn)生任何影響。

1.1.1 核心特性概覽
Mybatis-Plus具有以下核心特性:
- 無侵入:只做增強不做改變,引入它不會對現(xiàn)有工程產(chǎn)生影響
- 損耗小:啟動即會自動注入基本CURD,性能基本無損耗,直接面向?qū)ο蟛僮?/li>
- 強大的CRUD操作:內(nèi)置通用Mapper、通用Service,僅僅通過少量配置即可實現(xiàn)單表大部分CRUD操作
- 支持Lambda形式調(diào)用:通過Lambda表達式,方便的編寫各類查詢條件,無需再擔(dān)心字段寫錯
- 支持主鍵自動生成:支持多達4種主鍵策略,可自由配置,完美解決主鍵問題
- 支持ActiveRecord模式:支持ActiveRecord形式調(diào)用,實體類只需繼承Model類即可進行強大的CRUD操作
- 支持自定義全局通用操作:支持全局通用方法注入(Write once, use anywhere)
- 內(nèi)置代碼生成器:采用代碼或者Maven插件可快速生成Mapper、Model、Service、Controller層代碼
- 內(nèi)置分頁插件:基于Mybatis物理分頁,開發(fā)者無需關(guān)心具體操作,配置好插件之后,寫分頁等同于普通List查詢
- 分頁插件支持多種數(shù)據(jù)庫:支持MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer等多種數(shù)據(jù)庫
- 內(nèi)置性能分析插件:可輸出SQL語句以及其執(zhí)行時間,建議開發(fā)測試時啟用該功能,能快速揪出慢查詢
- 內(nèi)置全局?jǐn)r截插件:提供全表delete、update操作智能分析阻斷,也可自定義攔截規(guī)則,預(yù)防誤操作
1.1.2 架構(gòu)設(shè)計理念

Mybatis-Plus的架構(gòu)設(shè)計遵循以下原則:
// 設(shè)計理念示例:只做增強不做改變
public interface UserMapper extends BaseMapper<User> {
// 無需編寫任何代碼,即可獲得強大的CRUD功能
// BaseMapper提供了豐富的CRUD方法
}
// 傳統(tǒng)Mybatis方式
public interface TraditionalUserMapper {
int insert(User user);
int deleteById(Long id);
int updateById(User user);
User selectById(Long id);
List<User> selectList();
// 需要編寫大量重復(fù)的CRUD方法
}1.2 項目依賴配置與環(huán)境搭建
1.2.1 Maven依賴配置
在SpringBoot項目中集成Mybatis-Plus,首先需要添加相關(guān)依賴:
<!-- pom.xml -->
<dependencies>
<!-- SpringBoot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mybatis-Plus SpringBoot Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<!-- MySQL驅(qū)動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>1.2.2 Gradle依賴配置
對于使用Gradle的項目:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.3'
implementation 'mysql:mysql-connector-java'
implementation 'com.alibaba:druid-spring-boot-starter:1.2.16'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}1.3 數(shù)據(jù)庫連接與基礎(chǔ)配置
1.3.1 application.yml配置
# application.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
# Druid連接池配置
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
# Mybatis-Plus配置
mybatis-plus:
configuration:
# 開啟駝峰命名轉(zhuǎn)換
map-underscore-to-camel-case: true
# 開啟SQL日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 主鍵策略
id-type: ASSIGN_ID
# 邏輯刪除字段
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 掃描mapper文件
mapper-locations: classpath*:/mapper/**/*.xml
# 實體類別名包路徑
type-aliases-package: com.example.entity1.3.2 啟動類配置
@SpringBootApplication
@MapperScan("com.example.mapper")
public class MybatisPlusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusDemoApplication.class, args);
}
}1.3.3 Mybatis-Plus配置類
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
/**
* 分頁插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分頁插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(1000L);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
// 樂觀鎖插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
/**
* 自動填充配置
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MyMetaObjectHandler();
}
}
/**
* 自動填充處理器
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUser());
this.strictInsertFill(metaObject, "updateBy", String.class, getCurrentUser());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUser());
}
private String getCurrentUser() {
// 這里可以從SecurityContext或其他地方獲取當(dāng)前用戶
return "system";
}
}第二章:核心注解與基礎(chǔ)CRUD操作
2.1 實體類注解詳解
2.1.1 @TableName注解
@TableName注解用于指定實體類對應(yīng)的數(shù)據(jù)庫表名:
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private String password;
private String email;
private Integer age;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}2.1.2 @TableId注解詳解
@TableId注解用于標(biāo)識主鍵字段,支持多種主鍵生成策略:
public class IdTypeExample {
// 自動增長主鍵
@TableId(type = IdType.AUTO)
private Long autoId;
// 雪花算法ID(默認(rèn))
@TableId(type = IdType.ASSIGN_ID)
private Long snowflakeId;
// UUID
@TableId(type = IdType.ASSIGN_UUID)
private String uuidId;
// 手動輸入
@TableId(type = IdType.INPUT)
private Long inputId;
// 無主鍵策略
@TableId(type = IdType.NONE)
private Long noneId;
}2.1.3 @TableField注解應(yīng)用
@TableField注解用于配置字段的各種屬性:
@Data
@TableName("user_profile")
public class UserProfile {
@TableId
private Long id;
// 指定數(shù)據(jù)庫字段名
@TableField("user_name")
private String username;
// 字段不參與查詢
@TableField(select = false)
private String password;
// 字段不存在于數(shù)據(jù)庫
@TableField(exist = false)
private String fullName;
// 自動填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
// JSON字段處理
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> hobbies;
// 條件策略
@TableField(condition = SqlCondition.LIKE)
private String description;
}2.2 BaseMapper接口基礎(chǔ)操作
2.2.1 BaseMapper接口概述
BaseMapper接口提供了豐富的CRUD方法,無需編寫SQL即可完成常見的數(shù)據(jù)庫操作:
public interface UserMapper extends BaseMapper<User> {
// 繼承BaseMapper后,自動獲得以下方法:
// 插入操作:insert
// 刪除操作:deleteById, deleteBatchIds, deleteByMap, delete
// 更新操作:updateById, update
// 查詢操作:selectById, selectBatchIds, selectByMap, selectOne, selectCount, selectList, selectMaps, selectObjs, selectPage, selectMapsPage
}2.2.2 插入操作詳解
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* 單條插入
*/
public void insertUser() {
User user = new User();
user.setUsername("張三");
user.setPassword("123456");
user.setEmail("zhangsan@example.com");
user.setAge(25);
// 插入成功后,主鍵會自動回填到實體對象中
int result = userMapper.insert(user);
System.out.println("插入結(jié)果:" + result);
System.out.println("生成的主鍵:" + user.getId());
}
/**
* 批量插入(需要自定義實現(xiàn))
*/
public void batchInsert(List<User> userList) {
// Mybatis-Plus沒有提供批量插入的BaseMapper方法
// 可以使用Service層的saveBatch方法
// 或者自定義SQL實現(xiàn)真正的批量插入
userList.forEach(user -> userMapper.insert(user));
}
}2.2.3 查詢操作詳解
@Service
public class UserQueryService {
@Autowired
private UserMapper userMapper;
/**
* 根據(jù)ID查詢
*/
public User selectById(Long id) {
return userMapper.selectById(id);
}
/**
* 根據(jù)ID批量查詢
*/
public List<User> selectByIds(List<Long> ids) {
return userMapper.selectBatchIds(ids);
}
/**
* 根據(jù)Map條件查詢
*/
public List<User> selectByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age", 25);
columnMap.put("deleted", 0);
return userMapper.selectByMap(columnMap);
}
/**
* 查詢所有記錄
*/
public List<User> selectAll() {
return userMapper.selectList(null);
}
/**
* 條件查詢
*/
public List<User> selectByCondition() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 25)
.like("username", "張")
.isNotNull("email")
.orderByDesc("create_time");
return userMapper.selectList(queryWrapper);
}
/**
* 統(tǒng)計查詢
*/
public Long countUsers() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18);
return userMapper.selectCount(queryWrapper);
}
}2.2.4 更新操作詳解
@Service
public class UserUpdateService {
@Autowired
private UserMapper userMapper;
/**
* 根據(jù)ID更新
*/
public void updateById() {
User user = new User();
user.setId(1L);
user.setUsername("李四");
user.setAge(30);
// 只更新非null字段
userMapper.updateById(user);
}
/**
* 條件更新
*/
public void updateByCondition() {
User user = new User();
user.setAge(26);
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("username", "張三")
.set("email", "zhangsan_new@example.com");
userMapper.update(user, updateWrapper);
}
/**
* 直接設(shè)置字段值更新
*/
public void updateByWrapper() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("age", 25)
.set("age", 26)
.set("update_time", LocalDateTime.now());
userMapper.update(null, updateWrapper);
}
}2.2.5 刪除操作詳解
@Service
public class UserDeleteService {
@Autowired
private UserMapper userMapper;
/**
* 根據(jù)ID刪除
*/
public void deleteById(Long id) {
userMapper.deleteById(id);
}
/**
* 批量刪除
*/
public void deleteBatchIds(List<Long> ids) {
userMapper.deleteBatchIds(ids);
}
/**
* 根據(jù)Map條件刪除
*/
public void deleteByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age", 0);
columnMap.put("username", "test");
userMapper.deleteByMap(columnMap);
}
/**
* 條件刪除
*/
public void deleteByCondition() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lt("age", 18)
.isNull("email");
userMapper.delete(queryWrapper);
}
}2.3 Service層封裝與實戰(zhàn)應(yīng)用
2.3.1 IService接口介紹
Mybatis-Plus提供了IService接口,封裝了更多的CRUD操作:
public interface UserService extends IService<User> {
// 繼承IService后,自動獲得豐富的CRUD方法
// 如:save, saveBatch, saveOrUpdate, remove, update, get, list, page等
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// ServiceImpl已經(jīng)實現(xiàn)了IService的所有方法
// 可以直接使用,也可以重寫自定義實現(xiàn)
}2.3.2 Service層常用方法
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
/**
* 保存或更新
*/
public void saveOrUpdateUser(User user) {
// 根據(jù)主鍵判斷是插入還是更新
this.saveOrUpdate(user);
}
/**
* 批量保存
*/
public void batchSave(List<User> userList) {
// 批量插入,默認(rèn)批次大小為1000
this.saveBatch(userList);
// 自定義批次大小
this.saveBatch(userList, 500);
}
/**
* 條件查詢
*/
public List<User> getActiveUsers() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getDeleted, 0)
.gt(User::getAge, 18)
.orderByDesc(User::getCreateTime);
return this.list(queryWrapper);
}
/**
* 分頁查詢
*/
public IPage<User> getUserPage(int current, int size, String keyword) {
Page<User> page = new Page<>(current, size);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(keyword)) {
queryWrapper.like(User::getUsername, keyword)
.or()
.like(User::getEmail, keyword);
}
queryWrapper.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return this.page(page, queryWrapper);
}
/**
* 統(tǒng)計查詢
*/
public long countActiveUsers() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getDeleted, 0)
.gt(User::getAge, 18);
return this.count(queryWrapper);
}
}第三章:條件構(gòu)造器與復(fù)雜查詢
3.1 QueryWrapper查詢條件構(gòu)造
3.1.1 基礎(chǔ)條件構(gòu)造
QueryWrapper是Mybatis-Plus提供的查詢條件構(gòu)造器,支持鏈?zhǔn)秸{(diào)用:
@Service
public class QueryWrapperService {
@Autowired
private UserMapper userMapper;
/**
* 基礎(chǔ)條件查詢
*/
public List<User> basicQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 等值查詢
queryWrapper.eq("age", 25);
// 不等值查詢
queryWrapper.ne("username", "admin");
// 大于、小于查詢
queryWrapper.gt("age", 18)
.lt("age", 60);
// 大于等于、小于等于
queryWrapper.ge("create_time", "2023-01-01")
.le("create_time", "2023-12-31");
// 模糊查詢
queryWrapper.like("username", "張")
.likeLeft("email", "@qq.com") // %@qq.com
.likeRight("username", "admin"); // admin%
// 空值查詢
queryWrapper.isNull("deleted_time")
.isNotNull("email");
// 范圍查詢
queryWrapper.in("age", Arrays.asList(20, 25, 30))
.notIn("status", Arrays.asList(0, -1));
// 區(qū)間查詢
queryWrapper.between("age", 20, 30)
.notBetween("create_time", "2023-01-01", "2023-06-01");
return userMapper.selectList(queryWrapper);
}
/**
* 復(fù)雜條件組合
*/
public List<User> complexQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// (age > 18 AND age < 60) AND (username LIKE '%admin%' OR email IS NOT NULL)
queryWrapper.gt("age", 18)
.lt("age", 60)
.and(wrapper -> wrapper.like("username", "admin")
.or()
.isNotNull("email"));
// 嵌套查詢
queryWrapper.nested(wrapper -> wrapper.eq("status", 1)
.or()
.eq("status", 2));
return userMapper.selectList(queryWrapper);
}
/**
* 動態(tài)條件查詢
*/
public List<User> dynamicQuery(String username, Integer minAge, Integer maxAge, String email) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 條件動態(tài)拼接
queryWrapper.like(StringUtils.isNotBlank(username), "username", username)
.ge(minAge != null, "age", minAge)
.le(maxAge != null, "age", maxAge)
.eq(StringUtils.isNotBlank(email), "email", email);
return userMapper.selectList(queryWrapper);
}
}3.1.2 排序與分組
@Service
public class QueryOrderService {
@Autowired
private UserMapper userMapper;
/**
* 排序查詢
*/
public List<User> orderQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 單字段排序
queryWrapper.orderByAsc("age")
.orderByDesc("create_time");
// 多字段排序
queryWrapper.orderBy(true, true, "age", "username")
.orderBy(true, false, "create_time");
return userMapper.selectList(queryWrapper);
}
/**
* 分組查詢
*/
public List<Map<String, Object>> groupQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("age", "count(*) as count")
.groupBy("age")
.having("count(*) > 1")
.orderByDesc("count");
return userMapper.selectMaps(queryWrapper);
}
/**
* 字段選擇查詢
*/
public List<User> selectFields() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 選擇特定字段
queryWrapper.select("id", "username", "email", "age")
.eq("deleted", 0);
return userMapper.selectList(queryWrapper);
}
/**
* 排除字段查詢
*/
public List<User> excludeFields() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 排除敏感字段
queryWrapper.select(User.class, info -> !info.getColumn().equals("password"))
.eq("deleted", 0);
return userMapper.selectList(queryWrapper);
}
}3.2 UpdateWrapper更新條件構(gòu)造
3.2.1 基礎(chǔ)更新操作
@Service
public class UpdateWrapperService {
@Autowired
private UserMapper userMapper;
/**
* 條件更新
*/
public void updateByCondition() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
// 設(shè)置更新字段
updateWrapper.set("age", 26)
.set("update_time", LocalDateTime.now())
.eq("username", "張三")
.eq("deleted", 0);
userMapper.update(null, updateWrapper);
}
/**
* 字段自增更新
*/
public void incrementUpdate() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
// 年齡自增1
updateWrapper.setSql("age = age + 1")
.eq("id", 1L);
userMapper.update(null, updateWrapper);
}
/**
* 批量條件更新
*/
public void batchUpdate() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("status", 1)
.set("update_time", LocalDateTime.now())
.in("id", Arrays.asList(1L, 2L, 3L))
.eq("deleted", 0);
userMapper.update(null, updateWrapper);
}
/**
* 復(fù)雜條件更新
*/
public void complexUpdate() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("status", 2)
.set("remark", "批量更新")
.gt("age", 18)
.lt("age", 60)
.and(wrapper -> wrapper.like("username", "test")
.or()
.isNull("email"));
userMapper.update(null, updateWrapper);
}
}3.3 Lambda表達式條件構(gòu)造器
3.3.1 LambdaQueryWrapper使用
Lambda表達式條件構(gòu)造器提供了類型安全的字段引用,避免了字符串硬編碼:
@Service
public class LambdaQueryService {
@Autowired
private UserMapper userMapper;
/**
* Lambda查詢基礎(chǔ)用法
*/
public List<User> lambdaQuery() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 類型安全的字段引用
queryWrapper.eq(User::getAge, 25)
.like(User::getUsername, "張")
.isNotNull(User::getEmail)
.orderByDesc(User::getCreateTime);
return userMapper.selectList(queryWrapper);
}
/**
* 動態(tài)Lambda查詢
*/
public List<User> dynamicLambdaQuery(UserQueryDTO queryDTO) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(queryDTO.getUsername()),
User::getUsername, queryDTO.getUsername())
.eq(queryDTO.getAge() != null,
User::getAge, queryDTO.getAge())
.ge(queryDTO.getMinAge() != null,
User::getAge, queryDTO.getMinAge())
.le(queryDTO.getMaxAge() != null,
User::getAge, queryDTO.getMaxAge())
.eq(StringUtils.isNotBlank(queryDTO.getEmail()),
User::getEmail, queryDTO.getEmail())
.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userMapper.selectList(queryWrapper);
}
/**
* Lambda分頁查詢
*/
public IPage<User> lambdaPageQuery(int current, int size, UserQueryDTO queryDTO) {
Page<User> page = new Page<>(current, size);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 構(gòu)建查詢條件
queryWrapper.like(StringUtils.isNotBlank(queryDTO.getUsername()),
User::getUsername, queryDTO.getUsername())
.eq(queryDTO.getStatus() != null,
User::getStatus, queryDTO.getStatus())
.between(queryDTO.getStartTime() != null && queryDTO.getEndTime() != null,
User::getCreateTime, queryDTO.getStartTime(), queryDTO.getEndTime())
.eq(User::getDeleted, 0);
// 排序
if (StringUtils.isNotBlank(queryDTO.getOrderBy())) {
if ("age".equals(queryDTO.getOrderBy())) {
queryWrapper.orderBy(true, queryDTO.isAsc(), User::getAge);
} else if ("createTime".equals(queryDTO.getOrderBy())) {
queryWrapper.orderBy(true, queryDTO.isAsc(), User::getCreateTime);
}
} else {
queryWrapper.orderByDesc(User::getCreateTime);
}
return userMapper.selectPage(page, queryWrapper);
}
}
/**
* 查詢DTO
*/
@Data
public class UserQueryDTO {
private String username;
private Integer age;
private Integer minAge;
private Integer maxAge;
private String email;
private Integer status;
private LocalDateTime startTime;
private LocalDateTime endTime;
private String orderBy;
private boolean asc = false;
}3.3.2 LambdaUpdateWrapper使用
@Service
public class LambdaUpdateService {
@Autowired
private UserMapper userMapper;
/**
* Lambda更新操作
*/
public void lambdaUpdate() {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(User::getAge, 26)
.set(User::getUpdateTime, LocalDateTime.now())
.eq(User::getUsername, "張三")
.eq(User::getDeleted, 0);
userMapper.update(null, updateWrapper);
}
/**
* 條件Lambda更新
*/
public void conditionalLambdaUpdate(Long userId, UserUpdateDTO updateDTO) {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
// 動態(tài)設(shè)置更新字段
updateWrapper.set(StringUtils.isNotBlank(updateDTO.getUsername()),
User::getUsername, updateDTO.getUsername())
.set(updateDTO.getAge() != null,
User::getAge, updateDTO.getAge())
.set(StringUtils.isNotBlank(updateDTO.getEmail()),
User::getEmail, updateDTO.getEmail())
.set(User::getUpdateTime, LocalDateTime.now())
.eq(User::getId, userId)
.eq(User::getDeleted, 0);
userMapper.update(null, updateWrapper);
}
}
/**
* 更新DTO
*/
@Data
public class UserUpdateDTO {
private String username;
private Integer age;
private String email;
private Integer status;
}第四章:代碼生成器與自動化開發(fā)

4.1 代碼生成器配置與使用
4.1.1 代碼生成器依賴
首先需要添加代碼生成器相關(guān)依賴:
<!-- 代碼生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!-- 模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>4.1.2 基礎(chǔ)代碼生成器配置
public class CodeGenerator {
public static void main(String[] args) {
// 代碼生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("your-name");
gc.setOpen(false);
gc.setFileOverride(true);
gc.setServiceName("%sService");
gc.setIdType(IdType.ASSIGN_ID);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 數(shù)據(jù)源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("system");
pc.setParent("com.example");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 自定義配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
this.setMap(map);
}
};
// 自定義輸出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定義配置會被優(yōu)先輸出
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude("sys_user", "sys_role", "sys_permission");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
strategy.setLogicDeleteFieldName("deleted");
strategy.setVersionFieldName("version");
strategy.setEntityTableFieldAnnotationEnable(true);
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
}4.1.3 高級代碼生成器配置
@Component
public class AdvancedCodeGenerator {
/**
* 交互式代碼生成器
*/
public void interactiveGenerate() {
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入表名(多個表名用逗號分隔):");
String tableNames = scanner.nextLine();
System.out.println("請輸入模塊名:");
String moduleName = scanner.nextLine();
System.out.println("請輸入作者名:");
String author = scanner.nextLine();
generateCode(tableNames, moduleName, author);
}
/**
* 代碼生成核心方法
*/
private void generateCode(String tableNames, String moduleName, String author) {
// 代碼生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = buildGlobalConfig(author);
mpg.setGlobalConfig(gc);
// 數(shù)據(jù)源配置
DataSourceConfig dsc = buildDataSourceConfig();
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = buildPackageConfig(moduleName);
mpg.setPackageInfo(pc);
// 自定義配置
InjectionConfig cfg = buildInjectionConfig(pc);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = buildTemplateConfig();
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = buildStrategyConfig(tableNames, pc);
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
private GlobalConfig buildGlobalConfig(String author) {
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor(author);
gc.setOpen(false);
gc.setFileOverride(true);
gc.setServiceName("%sService");
gc.setServiceImplName("%sServiceImpl");
gc.setMapperName("%sMapper");
gc.setXmlName("%sMapper");
gc.setControllerName("%sController");
gc.setIdType(IdType.ASSIGN_ID);
gc.setDateType(DateType.TIME_PACK);
gc.setSwagger2(true);
gc.setActiveRecord(false);
gc.setEnableCache(false);
gc.setBaseResultMap(true);
gc.setBaseColumnList(true);
return gc;
}
private DataSourceConfig buildDataSourceConfig() {
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
return dsc;
}
private PackageConfig buildPackageConfig(String moduleName) {
PackageConfig pc = new PackageConfig();
pc.setModuleName(moduleName);
pc.setParent("com.example");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
return pc;
}
private InjectionConfig buildInjectionConfig(PackageConfig pc) {
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
map.put("author", "Code Generator");
this.setMap(map);
}
};
// 自定義輸出配置
List<FileOutConfig> focList = new ArrayList<>();
String projectPath = System.getProperty("user.dir");
// 自定義Mapper XML輸出
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
// 自定義DTO輸出
focList.add(new FileOutConfig("/templates/dto.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/" + pc.getModuleName()
+ "/dto/" + tableInfo.getEntityName() + "DTO" + StringPool.DOT_JAVA;
}
});
// 自定義VO輸出
focList.add(new FileOutConfig("/templates/vo.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/" + pc.getModuleName()
+ "/vo/" + tableInfo.getEntityName() + "VO" + StringPool.DOT_JAVA;
}
});
cfg.setFileOutConfigList(focList);
return cfg;
}
private TemplateConfig buildTemplateConfig() {
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
return templateConfig;
}
private StrategyConfig buildStrategyConfig(String tableNames, PackageConfig pc) {
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude(tableNames.split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
strategy.setLogicDeleteFieldName("deleted");
strategy.setVersionFieldName("version");
strategy.setEntityTableFieldAnnotationEnable(true);
// 字段填充策略
List<TableFill> tableFillList = new ArrayList<>();
tableFillList.add(new TableFill("create_time", FieldFill.INSERT));
tableFillList.add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
tableFillList.add(new TableFill("create_by", FieldFill.INSERT));
tableFillList.add(new TableFill("update_by", FieldFill.INSERT_UPDATE));
strategy.setTableFillList(tableFillList);
return strategy;
}
}4.2 自定義模板與代碼規(guī)范
4.2.1 自定義Entity模板
創(chuàng)建自定義Entity模板 /templates/entity.java.vm:
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
#end
/**
* ${table.comment!} 實體類
*
* @author ${author}
* @since ${date}
*/
#if(${entityLombokModel})
@Data
#if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
#else
@EqualsAndHashCode(callSuper = false)
#end
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value = "${entity}對象", description = "${table.comment!}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end
private static final long serialVersionUID = 1L;
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
#if(${field.keyFlag})
#if(${field.keyIdentityFlag})
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
#elseif(${field.fill})
#if(${field.convert})
@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.annotationColumnName}")
#end
#if("${field.propertyType}" == "Integer")
@ApiModelProperty(value = "${field.comment}")
private ${field.propertyType} ${field.propertyName};
#else
@ApiModelProperty(value = "${field.comment}")
private ${field.propertyType} ${field.propertyName};
#end
#end
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
this.${field.propertyName} = ${field.propertyName};
}
#end
#end
#if(${activeRecord})
@Override
protected Serializable pkVal() {
#if(${keyPropertyName})
return this.${keyPropertyName};
#else
return null;
#end
}
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}4.2.2 自定義Controller模板
創(chuàng)建自定義Controller模板 /templates/controller.java.vm:
package ${package.Controller};
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
/**
* ${table.comment!} 前端控制器
*
* @author ${author}
* @since ${date}
*/
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
@Api(tags = "${table.comment!}管理")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end
@Autowired
private ${table.serviceName} ${table.entityPath}Service;
/**
* 分頁查詢
*/
@GetMapping("/page")
@ApiOperation("分頁查詢${table.comment!}")
public Result<Page<${entity}>> page(@ApiParam("當(dāng)前頁") @RequestParam(defaultValue = "1") Integer current,
@ApiParam("頁大小") @RequestParam(defaultValue = "10") Integer size) {
Page<${entity}> page = new Page<>(current, size);
return Result.success(${table.entityPath}Service.page(page));
}
/**
* 根據(jù)ID查詢
*/
@GetMapping("/{id}")
@ApiOperation("根據(jù)ID查詢${table.comment!}")
public Result<${entity}> getById(@ApiParam("主鍵ID") @PathVariable Long id) {
return Result.success(${table.entityPath}Service.getById(id));
}
/**
* 新增
*/
@PostMapping
@ApiOperation("新增${table.comment!}")
public Result<Boolean> save(@RequestBody ${entity} ${table.entityPath}) {
return Result.success(${table.entityPath}Service.save(${table.entityPath}));
}
/**
* 修改
*/
@PutMapping
@ApiOperation("修改${table.comment!}")
public Result<Boolean> updateById(@RequestBody ${entity} ${table.entityPath}) {
return Result.success(${table.entityPath}Service.updateById(${table.entityPath}));
}
/**
* 刪除
*/
@DeleteMapping("/{id}")
@ApiOperation("刪除${table.comment!}")
public Result<Boolean> removeById(@ApiParam("主鍵ID") @PathVariable Long id) {
return Result.success(${table.entityPath}Service.removeById(id));
}
}
#end4.3 批量生成與項目集成
4.3.1 批量生成工具類
@Component
public class BatchCodeGenerator {
/**
* 批量生成指定數(shù)據(jù)庫的所有表
*/
public void generateAllTables(String databaseName) {
List<String> tableNames = getAllTableNames(databaseName);
for (String tableName : tableNames) {
generateSingleTable(tableName, getModuleNameFromTable(tableName));
}
}
/**
* 根據(jù)配置文件批量生成
*/
public void generateByConfig(String configPath) {
try {
Properties config = new Properties();
config.load(new FileInputStream(configPath));
String tables = config.getProperty("tables");
String moduleName = config.getProperty("moduleName");
String author = config.getProperty("author");
String packageName = config.getProperty("packageName");
generateCodeWithConfig(tables, moduleName, author, packageName);
} catch (IOException e) {
throw new RuntimeException("讀取配置文件失敗", e);
}
}
/**
* 獲取數(shù)據(jù)庫所有表名
*/
private List<String> getAllTableNames(String databaseName) {
List<String> tableNames = new ArrayList<>();
String sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = ?";
try (Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, databaseName);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
tableNames.add(resultSet.getString("table_name"));
}
} catch (SQLException e) {
throw new RuntimeException("獲取表名失敗", e);
}
return tableNames;
}
/**
* 根據(jù)表名推斷模塊名
*/
private String getModuleNameFromTable(String tableName) {
if (tableName.startsWith("sys_")) {
return "system";
} else if (tableName.startsWith("user_")) {
return "user";
} else if (tableName.startsWith("order_")) {
return "order";
} else {
return "common";
}
}
/**
* 獲取數(shù)據(jù)庫連接
*/
private Connection getConnection() throws SQLException {
String url = "jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8";
String username = "root";
String password = "123456";
return DriverManager.getConnection(url, username, password);
}
/**
* 單表生成
*/
private void generateSingleTable(String tableName, String moduleName) {
AutoGenerator mpg = new AutoGenerator();
// 配置生成器
configureGenerator(mpg, tableName, moduleName);
// 執(zhí)行生成
mpg.execute();
System.out.println("表 " + tableName + " 代碼生成完成!");
}
/**
* 配置生成器
*/
private void configureGenerator(AutoGenerator mpg, String tableName, String moduleName) {
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("Mybatis-Plus Generator");
gc.setOpen(false);
gc.setFileOverride(true);
gc.setServiceName("%sService");
gc.setIdType(IdType.ASSIGN_ID);
gc.setDateType(DateType.TIME_PACK);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 數(shù)據(jù)源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(moduleName);
pc.setParent("com.example");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude(tableName);
strategy.setLogicDeleteFieldName("deleted");
strategy.setEntityTableFieldAnnotationEnable(true);
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
}
}4.3.2 Maven插件集成
在pom.xml中配置Mybatis-Plus代碼生成器插件:
<build>
<plugins>
<!-- Mybatis-Plus代碼生成器插件 -->
<plugin>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<configurationFile>src/main/resources/generator-config.xml</configurationFile>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>4.3.3 配置文件管理
創(chuàng)建生成器配置文件 generator.properties:
# 數(shù)據(jù)庫配置 jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8 jdbc.username=root jdbc.password=123456 # 生成配置 generator.author=Mybatis-Plus Generator generator.packageName=com.example generator.moduleName=system generator.tables=sys_user,sys_role,sys_permission # 輸出配置 generator.outputDir=/src/main/java generator.mapperXmlDir=/src/main/resources/mapper # 策略配置 generator.tablePrefix=sys_ generator.logicDeleteField=deleted generator.versionField=version generator.enableLombok=true generator.enableSwagger=true generator.enableRestController=true
第五章:高級特性與性能優(yōu)化
5.1 分頁插件與性能優(yōu)化
5.1.1 分頁插件配置
Mybatis-Plus提供了強大的分頁插件,支持多種數(shù)據(jù)庫:
@Configuration
public class MybatisPlusPageConfig {
/**
* 分頁插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分頁插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 設(shè)置數(shù)據(jù)庫類型
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 設(shè)置最大單頁限制數(shù)量,默認(rèn)500條,-1不受限制
paginationInnerInterceptor.setMaxLimit(1000L);
// 溢出總頁數(shù)后是否進行處理
paginationInnerInterceptor.setOverflow(false);
// 生成countSql優(yōu)化掉join現(xiàn)象
paginationInnerInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}5.1.2 分頁查詢實戰(zhàn)
@Service
public class UserPageService {
@Autowired
private UserMapper userMapper;
/**
* 基礎(chǔ)分頁查詢
*/
public IPage<User> basicPage(int current, int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, null);
}
/**
* 條件分頁查詢
*/
public IPage<User> conditionalPage(UserPageQuery query) {
Page<User> page = new Page<>(query.getCurrent(), query.getSize());
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(query.getUsername()),
User::getUsername, query.getUsername())
.eq(query.getStatus() != null,
User::getStatus, query.getStatus())
.between(query.getStartTime() != null && query.getEndTime() != null,
User::getCreateTime, query.getStartTime(), query.getEndTime())
.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userMapper.selectPage(page, queryWrapper);
}
/**
* 自定義分頁查詢
*/
public IPage<UserVO> customPage(UserPageQuery query) {
Page<UserVO> page = new Page<>(query.getCurrent(), query.getSize());
return userMapper.selectUserPage(page, query);
}
/**
* 不查詢總數(shù)的分頁
*/
public IPage<User> pageWithoutCount(int current, int size) {
Page<User> page = new Page<>(current, size, false);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userMapper.selectPage(page, queryWrapper);
}
}
/**
* 分頁查詢參數(shù)
*/
@Data
public class UserPageQuery {
private Integer current = 1;
private Integer size = 10;
private String username;
private Integer status;
private LocalDateTime startTime;
private LocalDateTime endTime;
private String orderBy;
private Boolean asc = false;
}
/**
* 用戶視圖對象
*/
@Data
public class UserVO {
private Long id;
private String username;
private String email;
private Integer age;
private String roleName;
private LocalDateTime createTime;
}5.1.3 自定義分頁查詢
在Mapper中定義自定義分頁查詢:
public interface UserMapper extends BaseMapper<User> {
/**
* 自定義分頁查詢
*/
IPage<UserVO> selectUserPage(Page<UserVO> page, @Param("query") UserPageQuery query);
/**
* 復(fù)雜統(tǒng)計分頁查詢
*/
IPage<UserStatVO> selectUserStatPage(Page<UserStatVO> page, @Param("query") UserStatQuery query);
}對應(yīng)的XML映射:
<!-- UserMapper.xml -->
<select id="selectUserPage" resultType="com.example.vo.UserVO">
SELECT
u.id,
u.username,
u.email,
u.age,
r.role_name,
u.create_time
FROM sys_user u
LEFT JOIN sys_user_role ur ON u.id = ur.user_id
LEFT JOIN sys_role r ON ur.role_id = r.id
WHERE u.deleted = 0
<if test="query.username != null and query.username != ''">
AND u.username LIKE CONCAT('%', #{query.username}, '%')
</if>
<if test="query.status != null">
AND u.status = #{query.status}
</if>
<if test="query.startTime != null and query.endTime != null">
AND u.create_time BETWEEN #{query.startTime} AND #{query.endTime}
</if>
ORDER BY u.create_time DESC
</select>
<select id="selectUserStatPage" resultType="com.example.vo.UserStatVO">
SELECT
DATE_FORMAT(u.create_time, '%Y-%m') as month,
COUNT(*) as userCount,
COUNT(CASE WHEN u.status = 1 THEN 1 END) as activeCount,
AVG(u.age) as avgAge
FROM sys_user u
WHERE u.deleted = 0
<if test="query.startTime != null and query.endTime != null">
AND u.create_time BETWEEN #{query.startTime} AND #{query.endTime}
</if>
GROUP BY DATE_FORMAT(u.create_time, '%Y-%m')
ORDER BY month DESC
</select>5.2 邏輯刪除與數(shù)據(jù)安全
5.2.1 邏輯刪除配置
@Configuration
public class LogicDeleteConfig {
/**
* 邏輯刪除配置
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
}在實體類中配置邏輯刪除字段:
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private String password;
private String email;
private Integer age;
/**
* 邏輯刪除字段
* 0-未刪除,1-已刪除
*/
@TableLogic
@TableField(value = "deleted", fill = FieldFill.INSERT)
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}5.2.2 邏輯刪除實戰(zhàn)應(yīng)用
@Service
public class LogicDeleteService {
@Autowired
private UserMapper userMapper;
/**
* 邏輯刪除單個用戶
*/
public boolean logicDeleteUser(Long userId) {
// 調(diào)用deleteById會自動進行邏輯刪除
return userMapper.deleteById(userId) > 0;
}
/**
* 批量邏輯刪除
*/
public boolean batchLogicDelete(List<Long> userIds) {
return userMapper.deleteBatchIds(userIds) > 0;
}
/**
* 條件邏輯刪除
*/
public boolean logicDeleteByCondition(Integer status) {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(User::getStatus, status)
.set(User::getDeleted, 1)
.set(User::getUpdateTime, LocalDateTime.now());
return userMapper.update(null, updateWrapper) > 0;
}
/**
* 查詢包含已刪除的數(shù)據(jù)
*/
public List<User> selectWithDeleted() {
// 使用原生SQL查詢,繞過邏輯刪除
return userMapper.selectList(new QueryWrapper<User>().last("/* 包含已刪除數(shù)據(jù) */"));
}
/**
* 恢復(fù)邏輯刪除的數(shù)據(jù)
*/
public boolean restoreUser(Long userId) {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(User::getId, userId)
.set(User::getDeleted, 0)
.set(User::getUpdateTime, LocalDateTime.now());
return userMapper.update(null, updateWrapper) > 0;
}
/**
* 物理刪除(真正刪除數(shù)據(jù))
*/
public boolean physicalDelete(Long userId) {
// 需要自定義SQL實現(xiàn)物理刪除
return userMapper.physicalDeleteById(userId) > 0;
}
}在Mapper中添加物理刪除方法:
public interface UserMapper extends BaseMapper<User> {
/**
* 物理刪除用戶
*/
@Delete("DELETE FROM sys_user WHERE id = #{userId}")
int physicalDeleteById(@Param("userId") Long userId);
/**
* 查詢包含已刪除的用戶
*/
@Select("SELECT * FROM sys_user WHERE id = #{userId}")
User selectByIdWithDeleted(@Param("userId") Long userId);
}5.3 自動填充與審計功能
5.3.1 自動填充配置
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
private static final Logger log = LoggerFactory.getLogger(MyMetaObjectHandler.class);
/**
* 插入時自動填充
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("開始插入填充...");
// 填充創(chuàng)建時間
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
// 填充更新時間
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 填充創(chuàng)建人
this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUserId());
// 填充更新人
this.strictInsertFill(metaObject, "updateBy", String.class, getCurrentUserId());
// 填充邏輯刪除字段
this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
// 填充版本號
this.strictInsertFill(metaObject, "version", Integer.class, 1);
}
/**
* 更新時自動填充
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("開始更新填充...");
// 填充更新時間
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 填充更新人
this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUserId());
}
/**
* 獲取當(dāng)前用戶ID
*/
private String getCurrentUserId() {
// 從Spring Security上下文獲取當(dāng)前用戶
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else {
return principal.toString();
}
}
} catch (Exception e) {
log.warn("獲取當(dāng)前用戶失敗", e);
}
// 從請求頭獲取用戶信息
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String userId = request.getHeader("X-User-Id");
if (StringUtils.isNotBlank(userId)) {
return userId;
}
} catch (Exception e) {
log.warn("從請求頭獲取用戶ID失敗", e);
}
return "system";
}
}5.3.2 審計實體基類
@Data
@MappedSuperclass
public abstract class BaseAuditEntity {
/**
* 創(chuàng)建時間
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新時間
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 創(chuàng)建人
*/
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
/**
* 更新人
*/
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 邏輯刪除
*/
@TableLogic
@TableField(value = "deleted", fill = FieldFill.INSERT)
private Integer deleted;
/**
* 版本號(樂觀鎖)
*/
@Version
@TableField(value = "version", fill = FieldFill.INSERT)
private Integer version;
}繼承審計基類的實體:
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_user")
public class User extends BaseAuditEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private String password;
private String email;
private Integer age;
private Integer status;
}5.3.3 樂觀鎖配置與使用
@Configuration
public class OptimisticLockerConfig {
/**
* 樂觀鎖插件
*/
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}樂觀鎖使用示例:
@Service
public class OptimisticLockService {
@Autowired
private UserMapper userMapper;
/**
* 樂觀鎖更新示例
*/
public boolean updateWithOptimisticLock(Long userId, String newEmail) {
// 先查詢獲取當(dāng)前版本號
User user = userMapper.selectById(userId);
if (user == null) {
return false;
}
// 更新數(shù)據(jù),版本號會自動+1
user.setEmail(newEmail);
int result = userMapper.updateById(user);
// result為0表示更新失?。ò姹咎枦_突)
return result > 0;
}
/**
* 重試機制的樂觀鎖更新
*/
public boolean updateWithRetry(Long userId, String newEmail, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
User user = userMapper.selectById(userId);
if (user == null) {
return false;
}
user.setEmail(newEmail);
int result = userMapper.updateById(user);
if (result > 0) {
return true;
}
// 短暫等待后重試
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
return false;
}
}第六章:總結(jié)與展望
6.1 知識點總結(jié)與技術(shù)擴展
通過本文的深入學(xué)習(xí),我們?nèi)嬲莆樟薓ybatis-Plus的核心功能和實戰(zhàn)應(yīng)用。讓我們來總結(jié)一下關(guān)鍵知識點:
6.1.1 核心知識點回顧
基礎(chǔ)配置與環(huán)境搭建
- Mybatis-Plus的"只做增強不做改變"設(shè)計理念
- SpringBoot集成配置和依賴管理
- 數(shù)據(jù)源配置和連接池優(yōu)化
- 全局配置和插件配置
實體注解與CRUD操作
@TableName、@TableId、@TableField等核心注解- BaseMapper接口提供的豐富CRUD方法
- IService接口的高級封裝和批量操作
- 自動填充和審計功能的實現(xiàn)
條件構(gòu)造器與復(fù)雜查詢
- QueryWrapper和UpdateWrapper的靈活使用
- Lambda表達式條件構(gòu)造器的類型安全特性
- 動態(tài)條件構(gòu)建和復(fù)雜查詢組合
- 分頁查詢和自定義SQL的結(jié)合
代碼生成器與自動化開發(fā)
- 代碼生成器的配置和自定義模板
- 批量生成和項目集成策略
- Maven插件和配置文件管理
- 企業(yè)級代碼規(guī)范和最佳實踐
高級特性與性能優(yōu)化
- 分頁插件的配置和性能優(yōu)化
- 邏輯刪除的實現(xiàn)和數(shù)據(jù)安全保障
- 樂觀鎖機制和并發(fā)控制
- 自動填充和審計日志功能
6.1.2 技術(shù)擴展與深入學(xué)習(xí)
微服務(wù)架構(gòu)中的應(yīng)用
// 分布式事務(wù)中的Mybatis-Plus應(yīng)用
@Service
@Transactional
public class DistributedUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private OrderService orderService;
/**
* 分布式事務(wù)場景下的用戶創(chuàng)建
*/
@GlobalTransactional
public void createUserWithOrder(User user, Order order) {
// 創(chuàng)建用戶
userMapper.insert(user);
// 調(diào)用訂單服務(wù)
orderService.createOrder(order);
}
}緩存集成與性能優(yōu)化
// Redis緩存集成
@Service
public class CachedUserService extends ServiceImpl<UserMapper, User> {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Cacheable(value = "user", key = "#id")
public User getById(Long id) {
return super.getById(id);
}
@CacheEvict(value = "user", key = "#user.id")
public boolean updateById(User user) {
return super.updateById(user);
}
}多數(shù)據(jù)源配置
@Configuration
public class MultiDataSourceConfig {
@Primary
@Bean("masterDataSource")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("slaveDataSource")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DynamicDataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", masterDataSource());
dataSourceMap.put("slave", slaveDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
return dynamicDataSource;
}
}6.2 學(xué)習(xí)資源與參考資料
6.2.1 官方文檔與權(quán)威資源
官方文檔
- Mybatis-Plus官方文檔 - 最權(quán)威的學(xué)習(xí)資源
- Mybatis官方文檔 - 理解底層原理
- Spring Boot官方文檔 - 集成配置參考
權(quán)威書籍推薦
- 《MyBatis從入門到精通》- 劉增輝著,系統(tǒng)學(xué)習(xí)MyBatis基礎(chǔ)
- 《Spring Boot實戰(zhàn)》- Craig Walls著,深入理解SpringBoot
- 《Java并發(fā)編程實戰(zhàn)》- Brian Goetz著,理解并發(fā)和鎖機制
- 《高性能MySQL》- Baron Schwartz著,數(shù)據(jù)庫性能優(yōu)化
6.2.2 優(yōu)質(zhì)技術(shù)博客與教程
技術(shù)社區(qū)推薦
- 掘金技術(shù)社區(qū) - 豐富的Mybatis-Plus實戰(zhàn)文章
- CSDN博客平臺 - 大量的技術(shù)分享和問題解答
- 博客園 - 深度技術(shù)文章和經(jīng)驗分享
- 思否SegmentFault - 問答社區(qū)和技術(shù)討論
開源項目學(xué)習(xí)
- RuoYi-Vue - 基于SpringBoot+Mybatis-Plus的管理系統(tǒng)
- Pig - 微服務(wù)架構(gòu)的快速開發(fā)平臺
- Jeecg-Boot - 低代碼開發(fā)平臺
- SpringBlade - 微服務(wù)架構(gòu)的開發(fā)平臺
6.2.3 工具與插件推薦
開發(fā)工具
- MybatisX插件 - IDEA中的Mybatis增強插件,提供XML和Mapper跳轉(zhuǎn)
- Free Mybatis Plugin - 免費的Mybatis插件,支持代碼生成
- MyBatis Log Plugin - SQL日志格式化插件
- Database Navigator - 數(shù)據(jù)庫管理和SQL執(zhí)行工具
性能監(jiān)控工具
- Druid監(jiān)控 - 阿里巴巴開源的數(shù)據(jù)庫連接池監(jiān)控
- P6Spy - SQL語句攔截和性能分析
- SkyWalking - 分布式系統(tǒng)性能監(jiān)控
- Arthas - Java應(yīng)用診斷工具
6.3 技術(shù)發(fā)展趨勢與實踐建議
6.3.1 技術(shù)發(fā)展趨勢分析
云原生數(shù)據(jù)庫支持
隨著云計算的發(fā)展,Mybatis-Plus正在加強對云原生數(shù)據(jù)庫的支持,包括:
- 多云數(shù)據(jù)庫適配
- Serverless數(shù)據(jù)庫集成
- 云數(shù)據(jù)庫的自動擴縮容支持
響應(yīng)式編程支持
// 未來可能的響應(yīng)式API設(shè)計
@Service
public class ReactiveUserService {
@Autowired
private ReactiveUserMapper userMapper;
public Mono<User> findById(Long id) {
return userMapper.selectById(id);
}
public Flux<User> findAll() {
return userMapper.selectAll();
}
}AI輔助開發(fā)
- 智能SQL優(yōu)化建議
- 自動化性能調(diào)優(yōu)
- 基于機器學(xué)習(xí)的查詢優(yōu)化
6.3.2 最佳實踐建議
性能優(yōu)化建議
- 合理使用分頁查詢,避免大數(shù)據(jù)量查詢
- 啟用SQL日志,監(jiān)控慢查詢并優(yōu)化
- 使用連接池監(jiān)控,及時發(fā)現(xiàn)連接泄露
- 合理設(shè)置緩存策略,提高查詢性能
代碼規(guī)范建議
- 統(tǒng)一實體類設(shè)計,繼承審計基類
- 規(guī)范命名約定,保持代碼可讀性
- 合理使用條件構(gòu)造器,避免復(fù)雜的嵌套查詢
- 編寫單元測試,保證代碼質(zhì)量
安全性建議
- 啟用邏輯刪除,保護重要數(shù)據(jù)
- 使用樂觀鎖,處理并發(fā)更新
- 參數(shù)校驗,防止SQL注入
- 敏感字段加密,保護用戶隱私
6.4 互動與討論
6.4.1 開放性問題探討
讓我們一起思考以下幾個深度技術(shù)問題:
問題1:性能優(yōu)化策略
在高并發(fā)場景下,如何平衡Mybatis-Plus的便利性和性能要求?你會選擇哪些優(yōu)化策略?
問題2:微服務(wù)架構(gòu)適配
在微服務(wù)架構(gòu)中,如何設(shè)計Mybatis-Plus的數(shù)據(jù)訪問層?如何處理分布式事務(wù)和數(shù)據(jù)一致性?
問題3:代碼生成器定制
如何根據(jù)企業(yè)的代碼規(guī)范,定制適合團隊的代碼生成器模板?有哪些最佳實踐?
問題4:多數(shù)據(jù)源管理
在復(fù)雜的業(yè)務(wù)場景中,如何優(yōu)雅地管理多數(shù)據(jù)源?如何實現(xiàn)讀寫分離和數(shù)據(jù)庫切換?
問題5:未來發(fā)展方向
你認(rèn)為Mybatis-Plus在云原生和響應(yīng)式編程方面應(yīng)該如何發(fā)展?有哪些期待的新特性?
6.4.2 實戰(zhàn)挑戰(zhàn)項目
挑戰(zhàn)項目1:企業(yè)級用戶管理系統(tǒng)
使用Mybatis-Plus構(gòu)建一個完整的用戶管理系統(tǒng),包括:
- 用戶注冊、登錄、權(quán)限管理
- 分頁查詢、條件搜索、批量操作
- 審計日志、邏輯刪除、樂觀鎖
- 性能監(jiān)控、SQL優(yōu)化、緩存集成
挑戰(zhàn)項目2:微服務(wù)數(shù)據(jù)訪問層
設(shè)計一個微服務(wù)架構(gòu)的數(shù)據(jù)訪問層,實現(xiàn):
- 多數(shù)據(jù)源動態(tài)切換
- 分布式事務(wù)管理
- 讀寫分離和負載均衡
- 數(shù)據(jù)同步和一致性保證
6.4.3 社區(qū)交流與學(xué)習(xí)
技術(shù)交流方式
- GitHub討論區(qū) - 參與開源項目討論,提交Issue和PR
- 技術(shù)QQ群/微信群 - 加入Mybatis-Plus技術(shù)交流群
- 技術(shù)會議和Meetup - 參加線下技術(shù)分享活動
- 博客和視頻分享 - 分享自己的學(xué)習(xí)心得和實戰(zhàn)經(jīng)驗
持續(xù)學(xué)習(xí)建議
- 關(guān)注官方動態(tài) - 及時了解新版本特性和更新
- 實踐項目應(yīng)用 - 在實際項目中應(yīng)用所學(xué)知識
- 參與開源貢獻 - 為開源項目貢獻代碼和文檔
- 分享學(xué)習(xí)心得 - 通過博客、視頻等方式分享經(jīng)驗
結(jié)語
Mybatis-Plus作為優(yōu)秀的持久層增強工具,極大地提升了Java開發(fā)者的工作效率。通過本文的深入學(xué)習(xí),相信你已經(jīng)掌握了從基礎(chǔ)配置到高級特性的全面知識。
技術(shù)的學(xué)習(xí)是一個持續(xù)的過程,希望這篇文章能夠成為你Mybatis-Plus學(xué)習(xí)路上的重要參考。在實際項目中,要根據(jù)具體業(yè)務(wù)需求選擇合適的技術(shù)方案,在便利性和性能之間找到最佳平衡點。
到此這篇關(guān)于Mybatis-Plus 實戰(zhàn)使用與最佳實踐的文章就介紹到這了,更多相關(guān)Mybatis-Plus 使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mybatisplus條件構(gòu)造器的方法使用
- 使用MybatisPlus實現(xiàn)sql日志打印優(yōu)化
- mybatisPlus中apply的使用以進行聯(lián)表等復(fù)雜sql語句詳解
- 使用mybatisPlus的queryWrapper做左聯(lián)接,內(nèi)聯(lián)接方式
- SpringBoot如何使用MyBatisPlus逆向工程自動生成代碼
- Springboot使用MybatisPlus實現(xiàn)mysql樂觀鎖
- MybatisPlus中saveBatch方法的使用
- SpringBoot項目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫的過程
相關(guān)文章
Java SpringBoot詳解集成以及配置Swagger流程
Swagger 是一個規(guī)范和完整的框架,用于生成、描述、調(diào)用和可視化 RESTful 風(fēng)格的 Web 服務(wù)。總體目標(biāo)是使客戶端和文件系統(tǒng)作為服務(wù)器以同樣的速度來更新。文件的方法,參數(shù)和模型緊密集成到服務(wù)器端的代碼,允許API來始終保持同步2021-10-10
Java Arraylist在多線程環(huán)境下的問題與解決方案
這篇文章主要介紹了關(guān)于arraylist的多線程問題以及多種場景下的解決方案,同時詳細簡述了其核心內(nèi)容和原理,希望對大家有一定的幫助2026-03-03
bool當(dāng)成函數(shù)參數(shù)錯誤理解
經(jīng)常會在函數(shù)的參數(shù)里使用bool參數(shù),這會大大地降低代碼的可讀性2012-11-11
Java如何使用Set接口存儲沒有重復(fù)元素的數(shù)組
Set是一個繼承于Collection的接口,即Set也是集合中的一種。Set是沒有重復(fù)元素的集合,本篇我們就用它存儲一個沒有重復(fù)元素的數(shù)組2022-04-04

