前言

本篇是承接上一篇写的续篇,不再着重介绍mp的基础使用和查询,而主要是在一些应用功能,配置上面做一些介绍。

一:activeRecord 数据访问设计模式

应用Active Record 时,每一个类的实例对象唯一对应一个数据库表的一行(一对一关系)。你只需继承一个abstract Active Record 类就可以使用该设计模式访问数据库

实体类继承Model 仍然要写mapper 但是在应用中不需要导入
  1. public class User extends Model<User> {
  2. }

Model的方法
3432379039-5e801a2726a37_fix732.png

基础的增删改查

新增or更新

  1. @Test
  2. public void testInsert() {
  3. //INSERT INTO tb_user (user_name, age) VALUES (?, ?)
  4. User user = new User("xiahoudun", 24);
  5. boolean insert = user.insert();
  6. //当原有记录存在时(id存在)则是更新操作
  7. // boolean update = user.insertOrUpdate();
  8. System.err.println(insert);
  9. }

删除

  1. @Test
  2. public void testDelete() {
  3. //DELETE FROM tb_user WHERE id = ?
  4. User user = new User();
  5. user.setId(13L);
  6. //A根据传入的实体的id删除
  7. //boolean delete = user.deleteById();
  8. //B直接根据id删除
  9. //boolean delete2 = new User().deleteById(13L);
  10. //C根据传入的条件删除
  11. QueryWrapper<User> wrapper = new QueryWrapper<>();
  12. wrapper.eq("user_name", "xiahoudun");
  13. //DELETE FROM tb_user WHERE (user_name = ?)
  14. boolean delete3 = new User().delete(wrapper);
  15. System.err.println(delete3);
  16. }

修改

  1. @Test
  2. public void testUpdate() {
  3. //构建实体类修改
  4. User user = new User();
  5. //修改的记录
  6. user.setId(15L);
  7. //修改内容
  8. user.setName("夏侯惇");
  9. user.setAge(34);
  10. //UPDATE tb_user SET name = ?, age = ? WHERE id = ?
  11. //boolean update = user.updateById();
  12. //根据条件修改
  13. QueryWrapper<User> wrapper = new QueryWrapper<>();
  14. User user1 = new User();
  15. //修改的内容
  16. user1.setUserName("xiahoudun");
  17. user1.setPassword("123");
  18. //修改条件
  19. wrapper.eq("id", 15L);
  20. //UPDATE tb_user SET name = ?, age = ? WHERE (id = ?)
  21. boolean update1 = user.update(wrapper);
  22. System.err.println(update1);
  23. }

根据id查询

  1. @Test
  2. public void testSelect() {
  3. //SELECT id, email AS mail, name, user_name, age FROM tb_user WHERE id = ?
  4. User user = new User();
  5. user.setId(15L);
  6. //根据实体类的id查询
  7. User user1 = user.selectById();
  8. //直接输入id查询
  9. User user2 = new User().selectById(15L);
  10. System.out.println(user2);
  11. }

其他查询
selectCount:查询记录数
selectAll:查询所有
selectOne:查询一个
selectPage:分页查询

  1. @Test
  2. public void testOther() {
  3. QueryWrapper<User> wrapper = new QueryWrapper<>();
  4. wrapper.gt("age", 24);
  5. //查询记录数
  6. //SELECT COUNT(1) FROM tb_user WHERE (age > ?)
  7. // Integer integer = new User().selectCount(wrapper);
  8. // System.err.println(integer);
  9. //查询所有
  10. //SELECT id, email AS mail, name, user_name, age FROM tb_user
  11. // List<User> users = new User().selectAll();
  12. // users.forEach(System.err::println);
  13. //根据条件查询一个 查询到多个取第一个
  14. //SELECT id, email AS mail, name, user_name, age FROM tb_user WHERE (age = ?)
  15. QueryWrapper<User> wrapper2 = new QueryWrapper<>();
  16. wrapper2.eq("age", 24);
  17. // User user2 = new User().selectOne(wrapper2);
  18. // System.err.println(user2);
  19. //分页查询
  20. //SELECT id, email AS mail, name, user_name, age FROM tb_user WHERE (age > ?) LIMIT ?,?
  21. IPage<User> userIPage = new User().selectPage(new Page<>(2L, 4), wrapper);
  22. System.err.println(userIPage.getRecords().toString());
  23. }

二:乐观锁插件

当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

2.1 在配置类或启动类中配置bean
  1. @Bean
  2. public OptimisticLockerInterceptor optimisticLockerInterceptor() {
  3. return new OptimisticLockerInterceptor();
  4. }

2.2 在数据库中要有version字段 在实体类中配置version

version支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1,newVersion 会回写到 entity 中
在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
仅支持 updateById(id) 与 update(entity, wrapper) 方法

  1. //实体类添加版本信息__乐观锁
  2. @Version
  3. private Integer version;
  1. @Test
  2. public void testVersion() {
  3. User user = new User(20L);
  4. User oldUser = user.selectById();
  5. //直接设置版本信息
  6. user.setVersion(oldUser.getVersion());
  7. //修改内容
  8. user.setAge(30);
  9. // UPDATE tb_user SET age = 29, version = 2 WHERE id = 20 AND version = 1
  10. boolean update = user.updateById();
  11. System.err.println(update);
  12. }

三:sql 注入器

3.1 定义自己的方法类
  1. public class FindAll extends AbstractMethod {
  2. @Override
  3. public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
  4. String sqlMethod = "findAll";
  5. String sql = "select * from " + tableInfo.getTableName();
  6. SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
  7. return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource, modelClass, tableInfo);
  8. }
  9. }

3.2 把自己定义的方法加到baseMapper中让能被扫到
  1. public class MyInjector extends DefaultSqlInjector {
  2. @Override
  3. public List<AbstractMethod> getMethodList() {
  4. //获得父类所有基础的baseMapper方法
  5. List<AbstractMethod> methodList = super.getMethodList();
  6. //添加自定义方法
  7. methodList.add(new FindAll());
  8. return methodList;
  9. }
  10. }

3.3 在配置类或启动类 配置SQL注入器
  1. @Bean
  2. public MyInjector mySqlInjector() {
  3. return new MyInjector();
  4. }

3.4 在自定义mapper类中扩充自己写的findAll()方法
  1. public interface MyBaseMapper<T> extends BaseMapper<T> {
  2. List<T> findAll();
  3. }

3.5 userMapper不在继承baseMapper而是继承了自定义的mapper类
  1. @Repository
  2. public interface UserMapper2 extends MyBaseMapper<User> {
  3. }

3.6 使用自定义的findAll()方法
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest~~~~
  3. public class MyApplicationTest6 {
  4. @Autowired
  5. UserMapper2 userMapper2;
  6. /**
  7. * 自定义方法
  8. *
  9. * slelect 不再是每个字段名称 而是自己定义的 select *
  10. * SELECT * FROM tb_user
  11. */
  12. @Test
  13. public void testFindAll() {
  14. List<User> users = userMapper2.findAll();
  15. users.forEach(System.err::println);
  16. }
  17. /**
  18. * baseMapper的方法
  19. * SELECT id, user_name, name, age, email AS mail, version, deleted, sex FROM tb_user
  20. */
  21. @Test
  22. public void testSelectOne() {
  23. List<User> users = userMapper2.selectList(null);
  24. users.forEach(System.err::println);
  25. }
  26. }

四:自动填充功能
有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据,比如密码、version等。在MP中提供了这样的功能,可以实现自动填充。

4.1 自定义MetaObjectHandler 新建时默认密码
  1. @Component
  2. public class MyMetaObjectHandler implements MetaObjectHandler {
  3. /**
  4. * 对字段插入时进行操作
  5. *
  6. * @param metaObject
  7. */
  8. @Override
  9. public void insertFill(MetaObject metaObject) {
  10. Object password = getFieldValByName("password", metaObject);
  11. if (null == password) {
  12. //密码为,自动填充888888
  13. setFieldValByName("password", "888888", metaObject);
  14. }
  15. }
  16. /**
  17. * 对字段更新时进行操作
  18. *
  19. * @param metaObject
  20. */
  21. @Override
  22. public void updateFill(MetaObject metaObject) {
  23. }
  24. }

4.2 对password字段进行注解
  1. //查询时不返回该字段的值
  2. //fill =FieldFill.INSERT 对插入密码的时候可以进行填充
  3. @TableField(select = false, fill = FieldFill.INSERT)
  4. private String password;

4.3 测试
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class MyApplicationTest7 {
  4. @Autowired
  5. private UserMapper userMapper;
  6. /**
  7. * 插入测试 默认插入密码
  8. * INSERT INTO tb_user (user_name, password, name, age) VALUES ('lidian', '888888', '李典', 26)
  9. */
  10. @Test
  11. public void testInsert() {
  12. User user = new User();
  13. user.setUserName("lidian");
  14. user.setName("李典");
  15. user.setAge(26);
  16. //返回改变的行数
  17. int insert = userMapper.insert(user);
  18. System.err.println("change:" + insert); //1
  19. }
  20. }

五:逻辑删除

开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,而并非真正的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免数据被真正的删除。

5.1 在yml文件中配置 如果是默认值可以不改
  1. #逻辑删除配置
  2. mybatis-plus:
  3. global-config:
  4. db-config:
  5. # 逻辑已删除值(默认为 1)
  6. logic-delete-value: 1
  7. # 逻辑未删除值(默认为 0)
  8. logic-not-delete-value: 0

5.2 在实体类上加上注解 数据库需要有该字段
  1. //逻辑删除
  2. @TableLogic
  3. private Integer deleted;

5.3 在真正执行操作时不是delete操作而是update操作
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class MyApplicationTest8 {
  4. @Autowired
  5. private UserMapper userMapper;
  6. /**
  7. * 逻辑删除
  8. * UPDATE tb_user SET deleted = 1 WHERE id = 5 AND deleted = 0
  9. */
  10. @Test
  11. public void testDel() {
  12. int del = userMapper.deleteById(5L);
  13. System.out.println(del);
  14. }
  15. /**
  16. * 测试查询
  17. * SELECT id, user_name, name, age, email AS mail, version, deleted FROM tb_user WHERE id = ? AND deleted = 0
  18. */
  19. @Test
  20. public void testSelect() {
  21. User user = userMapper.selectById(5L);
  22. System.out.println(user);//null
  23. }
  24. }

六:通用枚举

解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!

6.1 将性别使用枚举值配置
  1. public enum SexEnum implements IEnum<Integer> {
  2. MAN(1, "男"),
  3. WOMAN(2, "女");
  4. private int value;
  5. private String desc;
  6. SexEnum(int value, String desc) {
  7. this.value = value;
  8. this.desc = desc;
  9. }
  10. @Override
  11. public Integer getValue() {
  12. return this.value;
  13. }
  14. @Override
  15. public String toString() {
  16. return this.desc;
  17. }
  18. }

6.2 配置实体类
  1. //配置枚举值
  2. private SexEnum sex;

6.3 操作过程中value和dest会相互转换
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class MyApplicationTest9 {
  4. @Autowired
  5. private UserMapper userMapper;
  6. /**
  7. * 枚举 新增
  8. * INSERT INTO tb_user(user_name, password, name, age, sex) VALUES ('diaochan','888888','貂蝉',18,2)
  9. */
  10. @Test
  11. public void testInsert() {
  12. User user = new User();
  13. user.setName("貂蝉");
  14. user.setUserName("diaochan");
  15. user.setAge(18);
  16. user.setSex(SexEnum.WOMAN);
  17. int insert = userMapper.insert(user);
  18. System.out.println(insert);
  19. }
  20. /**
  21. * 枚举值也可以在wrapper条件中使用 value和dest会相互转换
  22. * SELECT id, user_name, name, age, email AS mail, version, deleted, sex FROM tb_user WHERE deleted = 0 AND sex = 2
  23. */
  24. @Test
  25. public void testSelect() {
  26. QueryWrapper<User> wrapper = new QueryWrapper<>();
  27. wrapper.eq("sex", SexEnum.WOMAN);
  28. List<User> users = userMapper.selectList(wrapper);
  29. //User(id=19, userName=diaochan, password=null, name=貂蝉, age=18, mail=null, address=null, version=1, deleted=0, sex=女)
  30. System.out.println(users.toString());
  31. }
  32. }

七:代码自动生成

  1. public class MysqlGenerator {
  2. /**
  3. * 读取控制台内容
  4. *
  5. * @param tip
  6. * @return
  7. */
  8. public static String scanner(String tip) {
  9. Scanner scanner = new Scanner(System.in);
  10. System.out.println(("请输入" + tip + ":"));
  11. if (scanner.hasNext()) {
  12. String ipt = scanner.next();
  13. if (StringUtils.isNotEmpty(ipt)) {
  14. return ipt;
  15. }
  16. }
  17. throw new MybatisPlusException("请输入正确的" + tip + "!");
  18. }
  19. /**
  20. * 运行
  21. *
  22. * @param args
  23. */
  24. public static void main(String[] args) {
  25. // 代码生成器
  26. AutoGenerator mpg = new AutoGenerator();
  27. // 全局配置
  28. GlobalConfig gc = new GlobalConfig();
  29. String projectPath = System.getProperty("user.dir");
  30. gc.setOutputDir(projectPath + "/src/main/java");
  31. gc.setAuthor("Rem");
  32. gc.setOpen(false);
  33. mpg.setGlobalConfig(gc);
  34. // 数据源配置
  35. DataSourceConfig dsc = new DataSourceConfig();
  36. dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf8");
  37. // dsc.setSchemaName("public");
  38. dsc.setDriverName("com.mysql.cj.jdbc.Driver");
  39. dsc.setUsername("root");
  40. dsc.setPassword("root");
  41. mpg.setDataSource(dsc);
  42. // 包配置
  43. PackageConfig pc = new PackageConfig();
  44. pc.setModuleName(scanner("模块名"));
  45. pc.setParent("com.hhz.mp.generator");
  46. mpg.setPackageInfo(pc);
  47. // 自定义配置
  48. InjectionConfig cfg = new InjectionConfig() {
  49. @Override
  50. public void initMap() {
  51. // to do nothing
  52. }
  53. };
  54. List<FileOutConfig> focList = new ArrayList<>();
  55. focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
  56. @Override
  57. public String outputFile(TableInfo tableInfo) {
  58. // 自定义输入文件名称
  59. return projectPath + "/mp/src/main/resources/mapper/ " + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
  60. }
  61. });
  62. cfg.setFileOutConfigList(focList);
  63. mpg.setCfg(cfg);
  64. mpg.setTemplate(new TemplateConfig().setXml(null));
  65. // 策略配置
  66. StrategyConfig strategy = new StrategyConfig();
  67. strategy.setNaming(NamingStrategy.underline_to_camel);
  68. strategy.setColumnNaming(NamingStrategy.underline_to_camel);
  69. strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseEntity");
  70. strategy.setEntityLombokModel(true);
  71. strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.BaseController");
  72. strategy.setInclude(scanner("表名"));
  73. strategy.setSuperEntityColumns("id");
  74. strategy.setControllerMappingHyphenStyle(true);
  75. strategy.setTablePrefix(pc.getModuleName() + "_");
  76. mpg.setStrategy(strategy);
  77. // 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
  78. mpg.setTemplateEngine(new FreemarkerTemplateEngine());
  79. mpg.execute();
  80. }
  81. }