前言:该文章是以狂神说MyBatisPlus为参考,用以复习!初学者同样可以在这篇文章学到东西!
B站:狂神说MyBatisPlus地址:https://www.bilibili.com/video/BV17E411N7KN
狂神牛逼,强烈安利狂神!
导入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>Latest Versionversion>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.16version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
<scope>providedscope>
dependency>
dependencies>
配置数据库等
yaml:
#数据库配置
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
#日志配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
properties:
# 数据库配置
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.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
@Test
public void testInsert(){
User user = new User();
user.setName("codeyuaiiao");
user.setAge(3);
user.setEmail("747557612@qq.com");
int result = userMapper.insert(user);
System.out.println(result);
System.out.println(user);
}
注意:插入的Id要是全局唯一Id
2.主键自增策略
默认 ID_WORKER 全局唯一id
对应数据库中的主键(uuid.自增id.雪花算法.redis.zookeeper)
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0.可以保证几乎全球唯一
我们需要配置主键自增:
@TableId(type = IdType.AUTO)
3.其余的自增方式解读:
public enum IdType {
AUTO(0),//数据库ID自增
NONE(1),//该类型为未设置主键类型
INPUT(2),//用户输入ID
//该类型可以通过自己注册自动填充插件进行填充
//以下3种类型、只有当插入对象ID 为空,才自动填充。
ID_WORKER(3),//全局唯一ID (idWorker)
UUID(4),//全局唯一ID (UUID)
ID_WORKER_STR(5);//字符串全局唯一ID (idWorker 的字符串表示)
注意这里的update的参数都是一个实体类,并不是Id!
@Test
public void testUpdate(){
User user = new User();
user.setId(2L);
user.setEmail("32134@qq.com");
user.setName("CJ");
int update = userMapper.updateById(user);
System.out.println(update);
}
传入的是user,但发现它确实是按照Id来更新数据的!
创建时间 . 修改时间! 这些个操作都是自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表:gmt_create .gmt_modified几乎所有的表都要配置上!而且需要自动化!
方式一:数据库级别
不推荐使用!因为要修改数据库!
1.在表中新增字段 create_time , update_time
2.实体类要对应上
重新新增,时间出来了!
方式二:代码级别
1.删除数据库默认值
2.实体类增加注解
@TableField(fill = FieldFill.INSERT) //在插入时自动执行填充创建时间
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
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("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill...");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
新增测试,成功!
乐观锁就是什么时候都认为不会出问题,干啥都不加锁,等出了问题再次更新值测试
悲观锁就是认为啥时候都会出问题,无论干啥都会上锁,然后操作
MP乐观锁实现
如下:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
1.添加version字段
2.同步实体类
//乐观锁注解
@Version
private Integer version;
3.注册组件 创建config包
// 这个扫描本来是在我们MybatisPlusApplication 主启动类中,现在我们把它放在配置类中
// 扫描mapper文件夹
@MapperScan("com.chen.mapper")
@EnableTransactionManagement // 自动开启事务管理
@Configuration // 配置类
public class MybatisPlusConfig {
//新版乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
4.测试一下单线程中的乐观锁是否生效
// 测试乐观锁单线程成功
@Test
public void testOptimisticLock(){
// 1、查询用户信息
User user = userMapper.selectById(1);
// 2、修改用户信息
user.setName("xiaoshaung");
user.setEmail("123123132@qq.com");
// 3、执行更新操作
userMapper.updateById(user);
}
看一下SQL语句:
确实添加有version这个条件!
5.测试一下多线程的乐观锁
/*
线程1 虽然执行了赋值语句,但是还没进行更新操作,线程2就插队了抢先更新了,
由于并发下,可能导致线程1执行不成功
如果没有乐观锁就会覆盖线程2的值
*/
// 测试乐观锁多线程失败
@Test
public void testOptimisticLock2(){
// 线程1
User user = userMapper.selectById(1);
user.setName("cangjian111");
user.setEmail("32143455@qq.com");
// 模拟另外一个线程执行了插队操作
// 线程2
User user2 = userMapper.selectById(1);
user2.setName("cagnjian222");
user2.setEmail("32143455@qq.com");
userMapper.updateById(user2);
// 自旋锁来多次尝试提交
userMapper.updateById(user);
}
111被吞掉!
测试查询有如下方法:
挨个测试一把!
//test select
@Test
public void testSelect(){
User user = userMapper.selectById(2L);
System.out.println(user);
}
//测试批量查询
@Test
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3,4));
users.forEach(System.out::println);
}
//测试按条件查询
@Test
public void testSelectMap(){
Map<String, Object> map = new HashMap<>();
map.put("name","cangbaobao");
map.put("age",6);
//List selectByMap(@Param(Constants.COLUMN_MAP) Map columnMap);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
物理删除:数据库中直接删除
逻辑删除:实际上没有真正删除数据,数据还存在!只是用了一个字段来区分,在每次查询的时候都加上这个字段的where判断,就能达到一个逻辑删除的效果! 回收站就是用了逻辑删除实现的。
逻辑删除的步骤:
1,增加数据库字段!
2.实体类同步!
// 逻辑删除注解
@TableLogic
private Integer deleteId;
3.配置逻辑是删除组件!
// 逻辑删除组件 高版本无序写这个配置 已经自动集成!
@Bean
public ISqlInjector iSqlInjector(){
return new LogicSqlInjector();
}
4.测试一下删除,发现有如下方法
这里直接用deleteById测试:
//test delete
@Test
public void testDelete(){
userMapper.deleteById(6L);
}
发现:它竟然走的是update操作!
之后正常的查询将无法查询到它,这就是逻辑查询!!!
Mybatis-Plus也内置了分页插件!
1.集成分页插件:
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
2.可以用了:
@Test
public void testPage(){
// > E selectPage(E page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page, null);
page.getRecords().forEach(System.out::println);
System.out.println("getCurrent()"+page.getCurrent());
System.out.println("page.getSize()"+page.getSize());
System.out.println("page.getTotal()"+page.getTotal());
}
3.
实际上调用的也是limit!
一些重要的方法:
eq :
eq("name", "老王")
—>name = '老王'
ne :
ne("name", "老王")
—>name <> '老王'
gt :
gt("age", 18)
—>age > 18
ge :
ge("age", 18)
—>age >= 18
lt :
lt("age", 18)
—>age < 18
le :
le("age", 18)
—>age <= 18
between :
between("age", 18, 30)
—>age between 18 and 30
not between :
notBetween("age", 18, 30)
—>age not between 18 and 30
like :
like("name", "王")
—>name like '%王%'
notLike :
notLike("name", "王")
—>name not like '%王%'
likeLeft :
likeLeft("name", "王")
—>name like '%王'
likeRight :
likeRight("name", "王")
—>name like '王%'
isNull :
isNull("name")
—>name is null
isNotNull :
isNotNull("name")
—>name is not null
…
还有好多,可以在官网上找到,写得都很详细!!!
一些测试:
@Test
public void test1(){
// 查询 name 不为空的用户,并且邮箱不为空的用户,年龄大于等于20
//控制台查询的语句:SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name IS NOT NULL AND email IS NOT NULL AND age >= ?)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNotNull("name")
.isNotNull("email")
.ge("age",20);
userMapper.selectList(queryWrapper).forEach(System.out::println);
}
@Test
public void test2(){
// 查询名字 CJ
//生成的SQL:SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name = ?)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name","CJ");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
@Test
public void test3(){
//查询年龄在21到30的
// sql : SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (age BETWEEN ? AND ?)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("age",21,30);
userMapper.selectList(queryWrapper).forEach(System.out::println);
}
@Test
public void test4(){
//模糊查询 查询名字中含有i的和email以3开头的
// sql :SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name LIKE %i% AND email LIKE 3%)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","i")
.likeRight("email","3");
userMapper.selectMaps(queryWrapper).forEach(System.out::println);
}
// userMapper.selectList(queryWrapper).forEach(System.out::println); selectList和selectMap的区别就是返回值形式的不同,一个是按照user表中属性顺序来,一个是安照map形式显示
@Test
public void test5(){
//连接查询(内查询) 查找id<6的
// sql :SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (id IN (select id from user where id < 6))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id","select id from user where id < 6");
userMapper.selectList(queryWrapper).forEach(System.out::println);
}
@Test
public void test6(){
//根据id倒叙排列
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
userMapper.selectList(queryWrapper).forEach(System.out::println);
}
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
有两个版本:3.5.1以下和以上
我用的MP是3.4.2的,所以采用历史版本:
1.导入依赖:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.4.1version>
dependency>
<dependency>
<groupId>org.apache.velocitygroupId>
<artifactId>velocity-engine-coreartifactId>
<version>2.3version>
dependency>
2.编写一个自动生成类:注意包不要导错了 基本都是generator下的包
// 代码自动生成器
public class AutoGeneratorTest {
public static void main(String[] args) {
// 需要构建一个 代码自动生成器 对象
AutoGenerator mpg = new AutoGenerator();
// 配置策略
// 1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("CJ");
gc.setOpen(false);
gc.setFileOverride(false); // 是否覆盖
gc.setServiceName("%Service"); // 服务接口,去Service的I前缀
gc.setIdType(IdType.ID_WORKER); // 主键生成策略
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
// 给代码自动生成器注入配置
mpg.setGlobalConfig(gc);
// 2、 设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 3、包的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("blog");
pc.setParent("com.chen");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.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"); // 逻辑删除字段
// 自动填充策略
TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 乐观锁
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true); // localhost:8080/hello_id_2
mpg.setStrategy(strategy);
// 执行
mpg.execute();
}
}
3.执行!
大功告成!!!
MP完结!!!!