前言
本篇是承接上一篇写的续篇,不再着重介绍mp的基础使用和查询,而主要是在一些应用功能,配置上面做一些介绍。
一:activeRecord 数据访问设计模式
应用Active Record 时,每一个类的实例对象唯一对应一个数据库表的一行(一对一关系)。你只需继承一个abstract Active Record 类就可以使用该设计模式访问数据库
实体类继承Model 仍然要写mapper 但是在应用中不需要导入
public class User extends Model<User> {
}
Model的方法
基础的增删改查
新增or更新
@Test
public 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);
}
删除
@Test
public 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);
}
修改
@Test
public 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查询
@Test
public 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:分页查询
@Test
public 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
@Bean
public 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) 方法
//实体类添加版本信息__乐观锁
@Version
private Integer version;
@Test
public 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 = 1
boolean update = user.updateById();
System.err.println(update);
}
三:sql 注入器
3.1 定义自己的方法类
public class FindAll extends AbstractMethod {
@Override
public 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 {
@Override
public List<AbstractMethod> getMethodList() {
//获得父类所有基础的baseMapper方法
List<AbstractMethod> methodList = super.getMethodList();
//添加自定义方法
methodList.add(new FindAll());
return methodList;
}
}
3.3 在配置类或启动类 配置SQL注入器
@Bean
public MyInjector mySqlInjector() {
return new MyInjector();
}
3.4 在自定义mapper类中扩充自己写的findAll()方法
public interface MyBaseMapper<T> extends BaseMapper<T> {
List<T> findAll();
}
3.5 userMapper不在继承baseMapper而是继承了自定义的mapper类
@Repository
public interface UserMapper2 extends MyBaseMapper<User> {
}
3.6 使用自定义的findAll()方法
@RunWith(SpringRunner.class)
@SpringBootTest~~~~
public class MyApplicationTest6 {
@Autowired
UserMapper2 userMapper2;
/**
* 自定义方法
*
* slelect 不再是每个字段名称 而是自己定义的 select *
* SELECT * FROM tb_user
*/
@Test
public 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
*/
@Test
public void testSelectOne() {
List<User> users = userMapper2.selectList(null);
users.forEach(System.err::println);
}
}
四:自动填充功能
有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据,比如密码、version等。在MP中提供了这样的功能,可以实现自动填充。
4.1 自定义MetaObjectHandler 新建时默认密码
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 对字段插入时进行操作
*
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
Object password = getFieldValByName("password", metaObject);
if (null == password) {
//密码为,自动填充888888
setFieldValByName("password", "888888", metaObject);
}
}
/**
* 对字段更新时进行操作
*
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
}
}
4.2 对password字段进行注解
//查询时不返回该字段的值
//fill =FieldFill.INSERT 对插入密码的时候可以进行填充
@TableField(select = false, fill = FieldFill.INSERT)
private String password;
4.3 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTest7 {
@Autowired
private UserMapper userMapper;
/**
* 插入测试 默认插入密码
* INSERT INTO tb_user (user_name, password, name, age) VALUES ('lidian', '888888', '李典', 26)
*/
@Test
public 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 在实体类上加上注解 数据库需要有该字段
//逻辑删除
@TableLogic
private Integer deleted;
5.3 在真正执行操作时不是delete操作而是update操作
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTest8 {
@Autowired
private UserMapper userMapper;
/**
* 逻辑删除
* UPDATE tb_user SET deleted = 1 WHERE id = 5 AND deleted = 0
*/
@Test
public 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
*/
@Test
public 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;
}
@Override
public Integer getValue() {
return this.value;
}
@Override
public String toString() {
return this.desc;
}
}
6.2 配置实体类
//配置枚举值
private SexEnum sex;
6.3 操作过程中value和dest会相互转换
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTest9 {
@Autowired
private UserMapper userMapper;
/**
* 枚举 新增
* INSERT INTO tb_user(user_name, password, name, age, sex) VALUES ('diaochan','888888','貂蝉',18,2)
*/
@Test
public 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
*/
@Test
public 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() {
@Override
public void initMap() {
// to do nothing
}
};
List<FileOutConfig> focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public 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();
}
}