MyBatis-Plus-使用
MyBatis Plus 完整使用指南
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
目录
- 简介
- 核心功能
- CRUD 接口
- 条件构造器
- 分页插件
- 自动填充
- 逻辑删除
- 通用枚举
- 类型处理器
- 代码生成器
- 配置说明
- 测试示例
- 最佳实践
简介
MyBatis-Plus 是一个 MyBatis 的增强工具,它提供了以下特性:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence)
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use everywhere )
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则
核心功能
CRUD 接口
MyBatis-Plus 提供了丰富的 CRUD 接口,分为 Mapper 层和 Service 层两种方式。
Mapper CRUD 接口
Mapper 层 CRUD 接口封装在 BaseMapper
接口中,泛型 T 为任意实体对象。
public interface BaseMapper<T> {// 插入一条记录int insert(T entity);// 根据 entity 条件,删除记录int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);// 删除(根据ID 批量删除)int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);// 根据 ID 删除int deleteById(Serializable id);// 根据 columnMap 条件,删除记录int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);// 根据 whereEntity 条件,更新记录int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);// 根据 ID 修改int updateById(@Param(Constants.ENTITY) T entity);// 根据 ID 查询T selectById(Serializable id);// 根据 entity 条件,查询一条记录T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 查询(根据ID 批量查询)List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);// 根据 entity 条件,查询全部记录List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 查询(根据 columnMap 条件)List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);// 根据 Wrapper 条件,查询全部记录List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 entity 条件,查询全部记录(并翻页)IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录(并翻页)IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询总记录数Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
Service CRUD 接口
Service 层 CRUD 接口封装在 IService
接口中,进一步封装了 CRUD 操作。
public interface IService<T> {// 插入一条记录(选择字段,策略插入)boolean save(T entity);// 插入(批量)boolean saveBatch(Collection<T> entityList);// 插入(批量)boolean saveBatch(Collection<T> entityList, int batchSize);// TableId 注解存在更新记录,否插入一条记录boolean saveOrUpdate(T entity);// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);// 批量修改插入boolean saveOrUpdateBatch(Collection<T> entityList);// 批量修改插入boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);// 根据 entity 条件,删除记录boolean remove(Wrapper<T> queryWrapper);// 根据 ID 删除boolean removeById(Serializable id);// 根据 columnMap 条件,删除记录boolean removeByMap(Map<String, Object> columnMap);// 删除(根据ID 批量删除)boolean removeByIds(Collection<? extends Serializable> idList);// 根据 UpdateWrapper 条件,更新记录 需要设置sqlsetboolean update(Wrapper<T> updateWrapper);// 根据 whereEntity 条件,更新记录boolean update(T entity, Wrapper<T> updateWrapper);// 根据 ID 选择修改boolean updateById(T entity);// 根据ID 批量更新boolean updateBatchById(Collection<T> entityList);// 根据ID 批量更新boolean updateBatchById(Collection<T> entityList, int batchSize);// 根据 ID 查询T getById(Serializable id);// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")T getOne(Wrapper<T> queryWrapper);// 根据 Wrapper,查询一条记录T getOne(Wrapper<T> queryWrapper, boolean throwEx);// 根据 Wrapper,查询一条记录Map<String, Object> getMap(Wrapper<T> queryWrapper);// 根据 Wrapper,查询一条记录<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);// 查询所有List<T> list();// 查询列表List<T> list(Wrapper<T> queryWrapper);// 查询(根据ID 批量查询)Collection<T> listByIds(Collection<? extends Serializable> idList);// 查询(根据 columnMap 条件)Collection<T> listByMap(Map<String, Object> columnMap);// 查询所有列表List<Map<String, Object>> listMaps();// 查询列表List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);// 查询全部记录List<Object> listObjs();// 查询全部记录<V> List<V> listObjs(Function<? super Object, V> mapper);// 根据 Wrapper 条件,查询全部记录List<Object> listObjs(Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);// 无条件分页查询IPage<T> page(IPage<T> page);// 条件分页查询IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);// 无条件分页查询IPage<Map<String, Object>> pageMaps(IPage<T> page);// 条件分页查询IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);// 查询总记录数int count();// 根据 Wrapper 条件,查询总记录数int count(Wrapper<T> queryWrapper);// 链式查询 普通QueryChainWrapper<T> query();// 链式查询 lambda 式。注意:不支持 KotlinLambdaQueryChainWrapper<T> lambdaQuery();// 链式更改 普通UpdateChainWrapper<T> update();// 链式更改 lambda 式。注意:不支持 KotlinLambdaUpdateChainWrapper<T> lambdaUpdate();
}
条件构造器
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。
AbstractWrapper
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类,用于生成 sql 的 where 条件。
常用方法:
// 全部eq(或个别isNull)
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)// 等于 =
eq(R column, Object val)
eq(boolean condition, R column, Object val)// 不等于 <>
ne(R column, Object val)
ne(boolean condition, R column, Object val)// 大于 >
gt(R column, Object val)
gt(boolean condition, R column, Object val)// 大于等于 >=
ge(R column, Object val)
ge(boolean condition, R column, Object val)// 小于 <
lt(R column, Object val)
lt(boolean condition, R column, Object val)// 小于等于 <=
le(R column, Object val)
le(boolean condition, R column, Object val)// BETWEEN 值1 AND 值2
between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)// NOT BETWEEN 值1 AND 值2
notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)// LIKE '%值%'
like(R column, Object val)
like(boolean condition, R column, Object val)// NOT LIKE '%值%'
notLike(R column, Object val)
notLike(boolean condition, R column, Object val)// LIKE '%值'
likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)// LIKE '值%'
likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)// 字段 IS NULL
isNull(R column)
isNull(boolean condition, R column)// 字段 IS NOT NULL
isNotNull(R column)
isNotNull(boolean condition, R column)// 字段 IN (value.get(0), value.get(1), ...)
in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)// 字段 NOT IN (value.get(0), value.get(1), ...)
notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)// 分组:GROUP BY 字段, ...
groupBy(R... columns)
groupBy(boolean condition, R... columns)// 排序:ORDER BY 字段, ... ASC
orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)// 排序:ORDER BY 字段, ... DESC
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)// 排序:ORDER BY 字段, ...
orderBy(boolean condition, boolean isAsc, R... columns)// 拼接 OR
or()
or(boolean condition)// OR 嵌套
or(Consumer<Param> consumer)
or(boolean condition, Consumer<Param> consumer)// AND 嵌套
and(Consumer<Param> consumer)
and(boolean condition, Consumer<Param> consumer)
QueryWrapper
用于查询的条件构造器。
@Test
public void testQueryWrapper() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.select("id", "name", "age") // 指定查询字段.eq("name", "Tom") // 等于条件.ge("age", 18) // 大于等于条件.orderByDesc("create_time"); // 排序List<User> users = userMapper.selectList(queryWrapper);
}
LambdaQueryWrapper
使用 Lambda 表达式,避免硬编码字段名。
@Test
public void testLambdaQueryWrapper() {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.select(User::getId, User::getName, User::getAge).eq(User::getName, "Tom").ge(User::getAge, 18).orderByDesc(User::getCreateTime);List<User> users = userMapper.selectList(queryWrapper);
}
UpdateWrapper
用于更新的条件构造器。
@Test
public void testUpdateWrapper() {UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.set("name", "Jerry") // 设置更新字段.set("update_time", new Date()) // 设置更新时间.eq("id", 1); // 更新条件userMapper.update(null, updateWrapper);
}
LambdaUpdateWrapper
使用 Lambda 表达式的更新条件构造器。
@Test
public void testLambdaUpdateWrapper() {LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.set(User::getName, "Jerry").set(User::getUpdateTime, new Date()).eq(User::getId, 1);userMapper.update(null, updateWrapper);
}
分页插件
MyBatis-Plus 内置了分页插件,可以方便地实现分页查询。
配置分页插件
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false// paginationInterceptor.setOverflow(false);// 设置最大单页限制数量,默认 500 条,-1 不受限制// paginationInterceptor.setLimit(500);// 开启 count 的 join 优化,只针对部分 left joinpaginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));return paginationInterceptor;}
}
使用分页插件
@Test
public void testPagination() {// 创建分页对象,参数1:当前页,参数2:每页显示条数Page<User> page = new Page<>(1, 10);// 创建查询条件QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.ge("age", 18);// 执行分页查询IPage<User> userIPage = userMapper.selectPage(page, queryWrapper);// 获取分页信息System.out.println("总页数:" + userIPage.getPages());System.out.println("总记录数:" + userIPage.getTotal());System.out.println("当前页:" + userIPage.getCurrent());System.out.println("每页显示条数:" + userIPage.getSize());System.out.println("是否有上一页:" + userIPage.hasPrevious());System.out.println("是否有下一页:" + userIPage.hasNext());// 获取数据List<User> records = userIPage.getRecords();records.forEach(System.out::println);
}
XML 自定义分页
// Mapper 接口
public interface UserMapper extends BaseMapper<User> {/*** 自定义分页查询* @param page 分页对象* @param state 状态* @return 分页结果*/IPage<User> selectPageVo(Page<?> page, Integer state);
}
<!-- UserMapper.xml -->
<select id="selectPageVo" resultType="com.example.entity.User">SELECT id,name,age FROM user WHERE state = #{state}
</select>
// Service 实现
public IPage<User> selectUserPage(Page<User> page, Integer state) {return userMapper.selectPageVo(page, state);
}
自动填充
MyBatis-Plus 支持自动填充功能,可以自动填充创建时间、更新时间等字段。
配置自动填充
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}
}
实体类配置
@Data
@TableName("user")
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;// 插入时自动填充@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;// 插入和更新时自动填充@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
}
逻辑删除
MyBatis-Plus 支持逻辑删除,删除操作会将记录标记为已删除而不是物理删除。
配置逻辑删除
# application.yml
mybatis-plus:global-config:db-config:logic-delete-field: deleted # 全局逻辑删除的实体字段名logic-delete-value: 1 # 逻辑已删除值logic-not-delete-value: 0 # 逻辑未删除值
实体类配置
@Data
@TableName("user")
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;// 逻辑删除字段@TableLogicprivate Integer deleted;
}
通用枚举
MyBatis-Plus 支持通用枚举,可以将枚举类型与数据库字段进行映射。
定义枚举
public enum StatusEnum {ENABLE(1, "启用"),DISABLE(0, "禁用");private final int code;private final String desc;StatusEnum(int code, String desc) {this.code = code;this.desc = desc;}@EnumValue // 标记数据库存的值是codeprivate final int code;public int getCode() {return code;}public String getDesc() {return desc;}
}
配置扫描枚举包
# application.yml
mybatis-plus:type-enums-package: com.example.enums
实体类使用枚举
@Data
@TableName("user")
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;// 使用枚举类型private StatusEnum status;
}
类型处理器
MyBatis-Plus 支持自定义类型处理器,用于处理复杂类型的字段。
JSON 字段处理
@Data
@TableName(autoResultMap = true) // 必须开启自动映射
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;// JSON 类型字段@TableField(typeHandler = JacksonTypeHandler.class)private Map<String, Object> extendInfo;
}
代码生成器
MyBatis-Plus 提供了代码生成器,可以快速生成 Entity、Mapper、Service、Controller 等代码。
添加依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.3.1</version>
</dependency><!-- 模板引擎 -->
<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version>
</dependency>
代码生成器配置
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);mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=utf8");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("password");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();pc.setModuleName("system");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("user", "role"); // 需要生成的表名strategy.setControllerMappingHyphenStyle(true);mpg.setStrategy(strategy);// 模板引擎配置mpg.setTemplateEngine(new VelocityTemplateEngine());mpg.execute();}
}
配置说明
MyBatis-Plus 提供了丰富的配置选项,可以通过 application.yml 进行配置:
mybatis-plus:# 检查配置文件是否存在check-config-location: false# mapper映射文件扫描路径mapper-locations: classpath*:/mapper/**/*.xml# 类型别名扫描包type-aliases-package: com.example.entity# 类型枚举扫描包type-enums-package: com.example.enums# 启动时是否执行ddl操作executor-type: simple# Configurationconfiguration:# 是否开启自动驼峰命名规则映射map-underscore-to-camel-case: true# 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存cache-enabled: false# 允许 JDBC 支持自动生成主键use-generated-keys: false# 配置默认的执行器default-executor-type: simple# 指定 MyBatis 所用日志的具体实现log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 全局配置global-config:# 是否开启 LOGObanner: true# 数据库相关配置db-config:# 主键类型id-type: auto# 表名前缀table-prefix: tbl_# 逻辑删除字段名logic-delete-field: deleted# 逻辑删除值logic-delete-value: 1# 逻辑未删除值logic-not-delete-value: 0# 字段策略field-strategy: not_null
测试示例
以下是一些完整的测试示例,展示了 MyBatis-Plus 的各种功能:
基础实体类
@Data
@TableName("user")
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;@TableLogicprivate Integer deleted;
}
Mapper 接口
@Mapper
public interface UserMapper extends BaseMapper<User> {/*** 自定义查询方法* @param name 姓名* @return 用户列表*/@Select("SELECT * FROM user WHERE name = #{name}")List<User> selectByName(@Param("name") String name);
}
Service 接口
public interface UserService extends IService<User> {/*** 根据姓名查询用户* @param name 姓名* @return 用户列表*/List<User> getUserByName(String name);
}
Service 实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic List<User> getUserByName(String name) {return userMapper.selectByName(name);}
}
测试类
@SpringBootTest
class MyBatisPlusTest {@Autowiredprivate UserService userService;@Autowiredprivate UserMapper userMapper;@Testvoid testInsert() {User user = new User();user.setName("Tom");user.setAge(20);user.setEmail("tom@example.com");boolean result = userService.save(user);assertTrue(result);assertNotNull(user.getId());}@Testvoid testSelectById() {User user = userService.getById(1L);assertNotNull(user);assertEquals("Tom", user.getName());}@Testvoid testSelectList() {// 查询所有用户List<User> users = userService.list();assertNotNull(users);// 条件查询QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.ge("age", 18);List<User> userList = userService.list(queryWrapper);assertNotNull(userList);}@Testvoid testUpdate() {User user = new User();user.setId(1L);user.setName("Jerry");boolean result = userService.updateById(user);assertTrue(result);User updatedUser = userService.getById(1L);assertEquals("Jerry", updatedUser.getName());}@Testvoid testDelete() {boolean result = userService.removeById(1L);assertTrue(result);}@Testvoid testLambdaQuery() {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getName, "Tom").ge(User::getAge, 18).orderByDesc(User::getCreateTime);List<User> users = userService.list(queryWrapper);assertNotNull(users);}@Testvoid testPagination() {Page<User> page = new Page<>(1, 5);QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.ge("age", 18);IPage<User> userPage = userService.page(page, queryWrapper);assertNotNull(userPage);assertTrue(userPage.getTotal() > 0);}@Testvoid testBatchOperation() {// 批量插入List<User> userList = new ArrayList<>();for (int i = 0; i < 5; i++) {User user = new User();user.setName("User" + i);user.setAge(20 + i);user.setEmail("user" + i + "@example.com");userList.add(user);}boolean result = userService.saveBatch(userList);assertTrue(result);// 批量更新userList.forEach(user -> user.setAge(user.getAge() + 1));boolean updateResult = userService.updateBatchById(userList);assertTrue(updateResult);}@Testvoid testChainQuery() {// 链式查询List<User> users = userService.lambdaQuery().eq(User::getName, "Tom").ge(User::getAge, 18).list();// 链式更新boolean updateResult = userService.lambdaUpdate().eq(User::getName, "Tom").set(User::getAge, 25).update();assertTrue(updateResult);// 链式删除boolean deleteResult = userService.lambdaUpdate().eq(User::getName, "Jerry").remove();assertTrue(deleteResult);}
}
最佳实践
-
合理使用注解:充分利用 MyBatis-Plus 提供的注解,如 @TableName、@TableId、@TableField 等,减少 XML 配置。
-
善用条件构造器:使用 QueryWrapper 和 LambdaQueryWrapper 构造复杂查询条件,避免手写 SQL。
-
分页查询优化:对于大数据量的查询,合理使用分页插件,并注意分页参数的设置。
-
自动填充功能:合理使用自动填充功能,自动处理创建时间、更新时间等字段。
-
逻辑删除:对于需要保留历史数据的场景,使用逻辑删除而不是物理删除。
-
通用枚举:使用通用枚举处理状态类字段,提高代码可读性和维护性。
-
代码生成器:对于标准的 CRUD 操作,使用代码生成器快速生成基础代码。
-
性能监控:在开发和测试阶段开启 SQL 日志,监控 SQL 执行情况。
-
事务管理:合理使用 Spring 的事务管理,保证数据一致性。
-
异常处理:合理处理数据库操作异常,提供友好的错误提示。