赞
踩
官网及文档:https://mp.baomidou.com/
数据库以官方例子为例
现有一张 User 表,其表结构如下:
其对应的数据库 Schema 脚本如下:
DROP TABLE IF EXISTS user;
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
其对应的数据库 Data 脚本如下:
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
引入依赖
<!--数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--mybatis-plus驱动--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
项目结构
这里使用ymal进行配置
spring:
datasource:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
# 配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
pojo
这里使用lombok编写,没有lombok要下载插件
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
mapper
要继承BaseMapper
@Repository
public interface UserMapper extends BaseMapper<User> {
//继承BaseMapper<T> 所有的CRUD已经编写完成
}
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
}
UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件
配置完日志
@Test
//测试插入
public void testInsert(){
User user = new User();
user.setName("张三");
user.setAge(18);
user.setEmail("123456@qq.com");
int result = userMapper.insert(user);
System.out.println(result);
System.out.println(user);
}
注意:这里没有写填写id
分布式系统唯一ID生成方法汇总:https://www.cnblogs.com/haoxinyue/p/5208136.html
通过结果我们可以发现 id会自动填写
通过结果我们可以发现这里默认的是ID_WORKER 全局唯一ID
实现的算法为雪花算法
雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。雪花算法支持的TPS可以达到419万左右(2^22*1000)。
这里使用的mybatis-plus版本为3.0.5 新版本应该对应ASSIGN_ID
我们在UserMapper上 对id 添加主键注解
点开@TableId源码可以看到以下主键自增策略
1.添加注解
2.数据库字段一定是要自增
3.测试
再次插入
发现主键生成策略已是自增
@Test
//测试更新
public void testUpdate(){
User user = new User();
user.setName("李四");
user.setId(5L);
user.setEmail("123456@qq.com");
int result = userMapper.updateById(user);//注意参数为User类型
System.out.println(result);
System.out.println(user);
}
结果
我们再次修改 这次修改加入年龄
我们可以发现sql是动态帮我们配置的
像创建时间、修改时间,这些操作一般都是自动化完成的,我们不希望手动更新
阿里巴巴开发手册规定:所有的数据库表中都要有:gmt_create gmt_modified 且需要自动化
方式一 修改数据库 (工作中一般不允许改动数据库)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
private Date gmtCreate;
private Date gmtModified;
}
3.再次更新
结果:
方式二 代码
1.先把默认值和更新去掉
2.实体类上增加注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
//字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
3.编写处理器处理这个注解
@Slf4j @Component //一定不要忘记把处理器加到IOC容器中 public class MyMetaObjectHandler implements MetaObjectHandler { //插入时的填充策略 @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill..."); //default MetaObjectHandler setFieldValByName(String fieldName字段名, Object fieldVal字段值, MetaObject metaObject) this.setFieldValByName("gmtCreate",new Date(),metaObject); this.setFieldValByName("gmtModified",new Date(),metaObject); } //更新时的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill..."); this.setFieldValByName("gmtModified",new Date(),metaObject); } }
4.测试
插入:
修改:
乐观锁:顾名思义十分乐观,认为不会出现问题,不上锁,如果出现了问题则再次更新值测试
悲观锁:顾名思义十分悲观,认为总会出现问题,无论干什么都要先上锁,再去操作
举个例子:
乐观锁:先查询 获得版本号-- version = 1
--A
update user set name = "张三" , version = version + 1
where id = 2 and version = 1
--B B线程抢先完成,这个时候version = 2, 会导致A线程修改失败!
update user set name = "张三" , version = version + 1
where id = 2 and version = 1
mybatis-plus中乐观锁插件
1.给数据库中添加version字段
2.修改实体类
@Data @AllArgsConstructor @NoArgsConstructor public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; @Version//代表这个是一个乐观锁 private Integer version; //字段添加填充内容 @TableField(fill = FieldFill.INSERT) private Date gmtCreate; @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmtModified;
3.注册组件
注意新版本配置不同 以官方文档为准
@MapperScan("com.choi.dao")//可以将Application的Mapper扫描放在配置类里
@EnableTransactionManagement//开启事务
@Configuration//配置类
public class MybatisPlusConfig {
//注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
4.测试
//测试乐观锁成功
@Test
public void testOptimisticLocker(){
//1.查询用户信息
User user = userMapper.selectById(1L);
//2.修改用户信息
user.setName("王五");
user.setEmail("123456@qq.com");
//3.执行更新操作
userMapper.updateById(user);
}
//测试乐观锁失败 @Test public void testOptimisticLocker2(){ //这里模拟多线程 //线程A User user1 = userMapper.selectById(1L); user1.setName("王五111"); user1.setEmail("123456@qq.com"); //B线程 User user2 = userMapper.selectById(1L); user2.setName("王五222"); user2.setEmail("123456@qq.com"); //B线程比A先完成 userMapper.updateById(user2); userMapper.updateById(user1); }
较为简单 则不放结果
//查询测试 //按ID查询 @Test public void testSelectById(){ User user = userMapper.selectById(1L); System.out.println(user); } //多ID查询 @Test public void testSelectBatchId(){ List<User> userList = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));//传入集合 userList.forEach(System.out::println); } // 条件查询 map @Test public void testSelectByMap(){ HashMap<String,Object> map = new HashMap<>(); //自定义查询 map.put("字段名","值") 多个map.put则是多条件查询 map.put("name","张三"); map.put("age",20); List<User> userList = userMapper.selectByMap(map); userList.forEach(System.out::println); }
分页在网站里面使用的十分之多
1.原始的limit进行分页
2.pageHelper第三方插件
3.mybatis-plus 分页插件
这里使用mybatis-plus分页插件(版本问题可能使用方式不同 具体以官网为准)
1.注册分页插件
@Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false // paginationInterceptor.setOverflow(false); // 设置最大单页限制数量,默认 500 条,-1 不受限制 // paginationInterceptor.setLimit(500); // 开启 count 的 join 优化,只针对部分 left join paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } ------------------------------------------------------------ //这里我们简化 直接返回即可 //注册分页插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
2.直接使用page对象即可
//分页查询
@Test
public void testPage(){
Page<User> page = new Page<>(1,5);//第一个参数:当前页 第二个参数:页大小
userMapper.selectPage(page, null);//wrapper先写null
page.getRecords().forEach(System.out::println);
System.out.println(page.getTotal());//一共有多少条数据
System.out.println(page.hasNext());//是否有下一页
System.out.println(page.hasPrevious());//是否有上一页
System.out.println(page.getPages());//一共有多少页
System.out.println(page.getCurrent());//当前是第几页
}
//删除测试 @Test public void testDeleteById(){ userMapper.deleteById(1460137473252642820L); } //ID批量删除 @Test public void testDeleteBatchId(){ userMapper.deleteBatchIds(Arrays.asList(1460137473252642819L,1460137473252642818L)); } //通过map删除 @Test public void testDeleteByMap(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","王五222"); userMapper.deleteByMap(map); }
物理删除:从数据库中直接移除
逻辑删除:再数据库中没有被移除,而是通过一个变量使他失效 如 deleted = 0 => deleted = 1
管理员可以查看被删除的记录,以防止数据的丢失,类似回收站
测试:
1.在数据库中增加 deleted字段
2.修改实体类
3.注册插件
新版本以官网为准
//逻辑删除
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
yaml
#配置逻辑删除
global-config:
db-config:
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
4.测试
按ID删除、批量删除和条件删除各执行一次
结果:
我们可以看到对应的sql语句也和物理删除不同:
本质走的是更新操作
我们这是进行一次查询所有信息
我们可以发现对应的查询sql语句也发生了变化
我们在平时的开发中,会遇到一些慢sql。
作用:性能分析拦截器,用于输出每条sql语句及其执行的时间
mybatis-plus提供性能分析插件,如果超过这个时间就停止运行
(dev测试环境要在yaml里配置)
spring:
profiles:
active: dev
1.导入插件
新版本配置方法以官网为准
//sql执行效率分析插件
@Bean
@Profile({"dev","test"})//设置dev test环境开启 保证效率
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1);//ms 设置sql执行的最大时间,超时则不执行
performanceInterceptor.setFormat(true);//是否开启格式化支持
return performanceInterceptor;
}
2.测试
这里我们执行一个查询所有信息 => 报错
通过官网我们可以看到:
测试一:
@Test
void contextLoads() {
//查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("email")
.ge("age",12);//g:大于 e:等于
userMapper.selectList(wrapper);//wrapper 对比 map
}
测试二:
//查询单个 名字叫Jack
@Test
public void test2(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","Jack");
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
测试三:
// 查询年龄20~30的用户有多少个
@Test
public void test3(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",20,30);
Integer count = userMapper.selectCount(wrapper);//查询结果数
System.out.println(count);
}
测试四:
// 查询年龄20~30的用户有谁
@Test
public void test4(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",20,30);
List<Object> user = userMapper.selectObjs(wrapper);
user.forEach(System.out::println);
}
同理 not between用法也类似
测试五
//模糊查询
@Test
public void test5(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name","张")
.notLike("age",1)
.likeLeft("email","m")//左模糊 %X
.likeRight("id",1);//右模糊 X%
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
测试六:
//子查询
@Test
public void test6(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//id在子查询中查出来
wrapper.inSql("id","select id from user where id < 4");
List<Object> user = userMapper.selectObjs(wrapper);
user.forEach(System.out::println);
}
测试七:
//排序
@Test
public void test7(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderBy(true,false,"age","id");//参数一:是否要排序 参数二:是否ASC 参数三:字段
userMapper.selectList(wrapper);
}
测试八:
//分组
@Test
public void test8(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("name","email").groupBy(true,"id");
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
最新配置方法以官方为准
1.首先将依赖和MybatisPlusConfig配置好
<!--数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--mybatis-plus驱动--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency>
public class MybatisPlusConfig { //注册乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); } //注册分页插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } //逻辑删除 @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); } //sql执行效率分析插件 @Bean @Profile({"dev","test"})//设置dev test环境开启 保证效率 public PerformanceInterceptor performanceInterceptor(){ PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(100);//ms 设置sql执行的最大时间,超时则不执行 performanceInterceptor.setFormat(true);//是否开启格式化支持 return performanceInterceptor; } }
2.新建一个类,配置代码生成器
public class MybatisPlusGenerator { public static void main(String[] args) { //需要构建一个 代码自动生成器 对象 AutoGenerator autoGenerator = new AutoGenerator(); //配置策略 //1、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir");//获取用户的目录 gc.setOutputDir(projectPath+"/src/main/java/")//输出路径 .setAuthor("choi")//作者名 .setOpen(false)//是否打开资源管理器 .setFileOverride(false)//是否覆盖 .setServiceName("%sService")//去Service的I前缀 .setIdType(IdType.ID_WORKER) .setDateType(DateType.ONLY_DATE) .setSwagger2(true); autoGenerator.setGlobalConfig(gc); //2、配置数据源 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUsername("root") .setPassword("123456") .setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8") .setDriverName("com.mysql.cj.jdbc.Driver") .setDbType(DbType.MYSQL); autoGenerator.setDataSource(dsc); //3、包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName("test")//模块名 .setParent("com.choi")//放在哪个包下 .setEntity("entity")//实体类 .setMapper("mapper")//mapper .setService("service")//service层 .setController("controller");//controller层 autoGenerator.setPackageInfo(pc); //4、策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("user");//设置要映射的表 strategy.setNaming(NamingStrategy.underline_to_camel);//下划线转驼峰 strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true);// 自动生成lombok strategy.setLogicDeleteFieldName("deleted");//逻辑删除 strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true);//localhost:8080/hello_id_2 //自动填充设置 TableFill gmt_creat = new TableFill("gmt_creat", FieldFill.INSERT); TableFill gmt_modified = new TableFill("gmt_modified",FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(gmt_creat); tableFills.add(gmt_modified); strategy.setTableFillList(tableFills);//注意传入的是一个list //乐观锁 strategy.setVersionFieldName("version"); autoGenerator.setStrategy(strategy); autoGenerator.execute();//执行 }
3.执行后我们可以看到
可以发现代码都已经生成好了,具体要生成哪个表,怎么生成 改参数即可
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。