前言
本篇是承接上一篇写的续篇,不再着重介绍mp的基础使用和查询,而主要是在一些应用功能,配置上面做一些介绍。
一:activeRecord 数据访问设计模式
应用Active Record 时,每一个类的实例对象唯一对应一个数据库表的一行(一对一关系)。你只需继承一个abstract Active Record 类就可以使用该设计模式访问数据库
实体类继承Model 仍然要写mapper 但是在应用中不需要导入
public class User extends Model<User> {}
Model的方法
基础的增删改查
新增or更新
@Testpublic void testInsert() {//INSERT INTO tb_user (user_name, age) VALUES (?, ?)User user = new User("xiahoudun", 24);boolean insert = user.insert();//当原有记录存在时(id存在)则是更新操作// boolean update = user.insertOrUpdate();System.err.println(insert);}
删除
@Testpublic void testDelete() {//DELETE FROM tb_user WHERE id = ?User user = new User();user.setId(13L);//A根据传入的实体的id删除//boolean delete = user.deleteById();//B直接根据id删除//boolean delete2 = new User().deleteById(13L);//C根据传入的条件删除QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("user_name", "xiahoudun");//DELETE FROM tb_user WHERE (user_name = ?)boolean delete3 = new User().delete(wrapper);System.err.println(delete3);}
修改
@Testpublic void testUpdate() {//构建实体类修改User user = new User();//修改的记录user.setId(15L);//修改内容user.setName("夏侯惇");user.setAge(34);//UPDATE tb_user SET name = ?, age = ? WHERE id = ?//boolean update = user.updateById();//根据条件修改QueryWrapper<User> wrapper = new QueryWrapper<>();User user1 = new User();//修改的内容user1.setUserName("xiahoudun");user1.setPassword("123");//修改条件wrapper.eq("id", 15L);//UPDATE tb_user SET name = ?, age = ? WHERE (id = ?)boolean update1 = user.update(wrapper);System.err.println(update1);}
根据id查询
@Testpublic void testSelect() {//SELECT id, email AS mail, name, user_name, age FROM tb_user WHERE id = ?User user = new User();user.setId(15L);//根据实体类的id查询User user1 = user.selectById();//直接输入id查询User user2 = new User().selectById(15L);System.out.println(user2);}
其他查询
selectCount:查询记录数
selectAll:查询所有
selectOne:查询一个
selectPage:分页查询
@Testpublic void testOther() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.gt("age", 24);//查询记录数//SELECT COUNT(1) FROM tb_user WHERE (age > ?)// Integer integer = new User().selectCount(wrapper);// System.err.println(integer);//查询所有//SELECT id, email AS mail, name, user_name, age FROM tb_user// List<User> users = new User().selectAll();// users.forEach(System.err::println);//根据条件查询一个 查询到多个取第一个//SELECT id, email AS mail, name, user_name, age FROM tb_user WHERE (age = ?)QueryWrapper<User> wrapper2 = new QueryWrapper<>();wrapper2.eq("age", 24);// User user2 = new User().selectOne(wrapper2);// System.err.println(user2);//分页查询//SELECT id, email AS mail, name, user_name, age FROM tb_user WHERE (age > ?) LIMIT ?,?IPage<User> userIPage = new User().selectPage(new Page<>(2L, 4), wrapper);System.err.println(userIPage.getRecords().toString());}
二:乐观锁插件
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
2.1 在配置类或启动类中配置bean
@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();}
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) 方法
//实体类添加版本信息__乐观锁@Versionprivate Integer version;
@Testpublic void testVersion() {User user = new User(20L);User oldUser = user.selectById();//直接设置版本信息user.setVersion(oldUser.getVersion());//修改内容user.setAge(30);// UPDATE tb_user SET age = 29, version = 2 WHERE id = 20 AND version = 1boolean update = user.updateById();System.err.println(update);}
三:sql 注入器
3.1 定义自己的方法类
public class FindAll extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {String sqlMethod = "findAll";String sql = "select * from " + tableInfo.getTableName();SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource, modelClass, tableInfo);}}
3.2 把自己定义的方法加到baseMapper中让能被扫到
public class MyInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList() {//获得父类所有基础的baseMapper方法List<AbstractMethod> methodList = super.getMethodList();//添加自定义方法methodList.add(new FindAll());return methodList;}}
3.3 在配置类或启动类 配置SQL注入器
@Beanpublic MyInjector mySqlInjector() {return new MyInjector();}
3.4 在自定义mapper类中扩充自己写的findAll()方法
public interface MyBaseMapper<T> extends BaseMapper<T> {List<T> findAll();}
3.5 userMapper不在继承baseMapper而是继承了自定义的mapper类
@Repositorypublic interface UserMapper2 extends MyBaseMapper<User> {}
3.6 使用自定义的findAll()方法
@RunWith(SpringRunner.class)@SpringBootTest~~~~public class MyApplicationTest6 {@AutowiredUserMapper2 userMapper2;/*** 自定义方法** slelect 不再是每个字段名称 而是自己定义的 select ** SELECT * FROM tb_user*/@Testpublic void testFindAll() {List<User> users = userMapper2.findAll();users.forEach(System.err::println);}/*** baseMapper的方法* SELECT id, user_name, name, age, email AS mail, version, deleted, sex FROM tb_user*/@Testpublic void testSelectOne() {List<User> users = userMapper2.selectList(null);users.forEach(System.err::println);}}
四:自动填充功能
有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据,比如密码、version等。在MP中提供了这样的功能,可以实现自动填充。
4.1 自定义MetaObjectHandler 新建时默认密码
@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler {/*** 对字段插入时进行操作** @param metaObject*/@Overridepublic void insertFill(MetaObject metaObject) {Object password = getFieldValByName("password", metaObject);if (null == password) {//密码为,自动填充888888setFieldValByName("password", "888888", metaObject);}}/*** 对字段更新时进行操作** @param metaObject*/@Overridepublic void updateFill(MetaObject metaObject) {}}
4.2 对password字段进行注解
//查询时不返回该字段的值//fill =FieldFill.INSERT 对插入密码的时候可以进行填充@TableField(select = false, fill = FieldFill.INSERT)private String password;
4.3 测试
@RunWith(SpringRunner.class)@SpringBootTestpublic class MyApplicationTest7 {@Autowiredprivate UserMapper userMapper;/*** 插入测试 默认插入密码* INSERT INTO tb_user (user_name, password, name, age) VALUES ('lidian', '888888', '李典', 26)*/@Testpublic void testInsert() {User user = new User();user.setUserName("lidian");user.setName("李典");user.setAge(26);//返回改变的行数int insert = userMapper.insert(user);System.err.println("change:" + insert); //1}}
五:逻辑删除
开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,而并非真正的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免数据被真正的删除。
5.1 在yml文件中配置 如果是默认值可以不改
#逻辑删除配置mybatis-plus:global-config:db-config:# 逻辑已删除值(默认为 1)logic-delete-value: 1# 逻辑未删除值(默认为 0)logic-not-delete-value: 0
5.2 在实体类上加上注解 数据库需要有该字段
//逻辑删除@TableLogicprivate Integer deleted;
5.3 在真正执行操作时不是delete操作而是update操作
@RunWith(SpringRunner.class)@SpringBootTestpublic class MyApplicationTest8 {@Autowiredprivate UserMapper userMapper;/*** 逻辑删除* UPDATE tb_user SET deleted = 1 WHERE id = 5 AND deleted = 0*/@Testpublic void testDel() {int del = userMapper.deleteById(5L);System.out.println(del);}/*** 测试查询* SELECT id, user_name, name, age, email AS mail, version, deleted FROM tb_user WHERE id = ? AND deleted = 0*/@Testpublic void testSelect() {User user = userMapper.selectById(5L);System.out.println(user);//null}}
六:通用枚举
解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!
6.1 将性别使用枚举值配置
public enum SexEnum implements IEnum<Integer> {MAN(1, "男"),WOMAN(2, "女");private int value;private String desc;SexEnum(int value, String desc) {this.value = value;this.desc = desc;}@Overridepublic Integer getValue() {return this.value;}@Overridepublic String toString() {return this.desc;}}
6.2 配置实体类
//配置枚举值private SexEnum sex;
6.3 操作过程中value和dest会相互转换
@RunWith(SpringRunner.class)@SpringBootTestpublic class MyApplicationTest9 {@Autowiredprivate UserMapper userMapper;/*** 枚举 新增* INSERT INTO tb_user(user_name, password, name, age, sex) VALUES ('diaochan','888888','貂蝉',18,2)*/@Testpublic void testInsert() {User user = new User();user.setName("貂蝉");user.setUserName("diaochan");user.setAge(18);user.setSex(SexEnum.WOMAN);int insert = userMapper.insert(user);System.out.println(insert);}/*** 枚举值也可以在wrapper条件中使用 value和dest会相互转换* SELECT id, user_name, name, age, email AS mail, version, deleted, sex FROM tb_user WHERE deleted = 0 AND sex = 2*/@Testpublic void testSelect() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("sex", SexEnum.WOMAN);List<User> users = userMapper.selectList(wrapper);//User(id=19, userName=diaochan, password=null, name=貂蝉, age=18, mail=null, address=null, version=1, deleted=0, sex=女)System.out.println(users.toString());}}
七:代码自动生成
public class MysqlGenerator {/*** 读取控制台内容** @param tip* @return*/public static String scanner(String tip) {Scanner scanner = new Scanner(System.in);System.out.println(("请输入" + tip + ":"));if (scanner.hasNext()) {String ipt = scanner.next();if (StringUtils.isNotEmpty(ipt)) {return ipt;}}throw new MybatisPlusException("请输入正确的" + tip + "!");}/*** 运行** @param args*/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("Rem");gc.setOpen(false);mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf8");// dsc.setSchemaName("public");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("root");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();pc.setModuleName(scanner("模块名"));pc.setParent("com.hhz.mp.generator");mpg.setPackageInfo(pc);// 自定义配置InjectionConfig cfg = new InjectionConfig() {@Overridepublic void initMap() {// to do nothing}};List<FileOutConfig> focList = new ArrayList<>();focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {@Overridepublic String outputFile(TableInfo tableInfo) {// 自定义输入文件名称return projectPath + "/mp/src/main/resources/mapper/ " + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;}});cfg.setFileOutConfigList(focList);mpg.setCfg(cfg);mpg.setTemplate(new TemplateConfig().setXml(null));// 策略配置StrategyConfig strategy = new StrategyConfig();strategy.setNaming(NamingStrategy.underline_to_camel);strategy.setColumnNaming(NamingStrategy.underline_to_camel);strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseEntity");strategy.setEntityLombokModel(true);strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.BaseController");strategy.setInclude(scanner("表名"));strategy.setSuperEntityColumns("id");strategy.setControllerMappingHyphenStyle(true);strategy.setTablePrefix(pc.getModuleName() + "_");mpg.setStrategy(strategy);// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!mpg.setTemplateEngine(new FreemarkerTemplateEngine());mpg.execute();}}
