文章目录
1.入门
使用MybatisPlus可以节省大量代码
1、创建数据库
2、创建user表
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)
);
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');
3、编写项目,初始化项目SpringBoot
4、导入依赖
注意:不要同时导入Mybatis和MybatisPlus
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--mybatisplus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
5、连接数据库
编写application.properties文件
#数据库连接配置
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#配置日志(控制台输出)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
6、传统:pojo-dao(连接mybatis、配置mapper.xml文件)-service-controller
现在:
-
pojo实体类:
package com.kuang.dao; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; //lombok插件 @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; //数据库主键 private String name; private Integer age; private String email; }
-
mapper接口:
package com.kuang.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.kuang.dao.User; import org.springframework.stereotype.Repository; @Repository //代表持久层 public interface UserMapper extends BaseMapper<User> { //所有的CRUD操作已经编写完成 //不在需要像以前的配置一大堆文件,比如MybatisUtils,UserMapper.xml等等 }
编写MybatisplusApplication文件
@MapperScan("com.kuang.mapper") //扫描mapper文件夹 @SpringBootApplication public class MybatisplusApplication { public static void main(String[] args) { SpringApplication.run(MybatisplusApplication.class, args); } }
-
使用测试:
@SpringBootTest class MybatisplusApplicationTests { //继承了BaseMapper,所有的方法都来自父类BaseMapper //也可以编写自己的扩展方法 @Autowired private UserMapper userMapper; @Test void contextLoads() { //查询全部用户 List<User> users = userMapper.selectList(null); //wrapper是条件构造器,先取值null users.forEach(System.out::println); } }
查看BaseMapper,它包含了许多CRUD方法
public interface BaseMapper<T> { int insert(T var1); int deleteById(Serializable var1); int deleteByMap(@Param("cm") Map<String, Object> var1); int delete(@Param("ew") Wrapper<T> var1); int deleteBatchIds(@Param("coll") Collection<? extends Serializable> var1); int updateById(@Param("et") T var1); int update(@Param("et") T var1, @Param("ew") Wrapper<T> var2); T selectById(Serializable var1); List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> var1); List<T> selectByMap(@Param("cm") Map<String, Object> var1); T selectOne(@Param("ew") Wrapper<T> var1); Integer selectCount(@Param("ew") Wrapper<T> var1); List<T> selectList(@Param("ew") Wrapper<T> var1); List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> var1); List<Object> selectObjs(@Param("ew") Wrapper<T> var1); IPage<T> selectPage(IPage<T> var1, @Param("ew") Wrapper<T> var2); IPage<Map<String, Object>> selectMapsPage(IPage<T> var1, @Param("ew") Wrapper<T> var2); }
7、测试结果
附项目结构图:
思考:
- SQL我们并没有写,MybatisPlus帮我们写了SQL。
- 方法哪里来的?MybatisPlus也帮我们写了。
2、插入测试
@Test
public void testInsert(){
User user = new User();
user.setName("小呱");
user.setAge(18);
//user.setId((long) 123); 这里先不设置主键Id,运行会发现MybatisPlus自动帮你生成了Id
user.setEmail("123@qq.com");
int result = userMapper.insert(user);
System.out.println(result); //返回受影响的行数
System.out.println(user);
}
结果:
这里在插入数据时并没有给id赋值,但是可以从运行结果可以看出,MP给我们赋了值
2.1、主键生成策略
推荐一篇博客:分布式系统唯一ID生成方案汇总
雪花算法(Snowflake)
Snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。
其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
几乎可以保证全球唯一。
2.2、主键自增
-
实体类字段上增加注解
@TableId(type = IdType.AUTO)
-
数据库字段一定要是自增
加上注解和自增设置完成后再次运行,会发现ID自动自增:
3、更新测试
3.1、基本测试
@Test
public void testUpdate(){
User user = new User();
//通过条件自动拼接动态SQL
user.setId(5L);
user.setName("小黄");
user.setAge(36);
int result = userMapper.updateById(user); //根据Id更新数据
System.out.println(result);
}
运行结果:
MybatisPlus可以通过条件自动拼接动态SQL
3.2、自动填充
第一种方法:数据库级别(不推荐)
1、在表中新增字段create_time、update_time(默认值设置为CURRENT_TIMESTAMP,图片里忘记设置了)
2、再次测试插入方法,需要先把实体类同步
private Date createTime;
private Date updateTime;
3、运行结果:
第二张方法:代码级别
1、删除数据库的默认值、更新操作
2、在实体类字段属性上增加注解
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
3、编写处理器来处理这个注解(新建com.kuang.handler.MyMetaObjectHandler )
package com.kuang.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component //一定不要忘记把处理器加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill...");
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);
}
}
4、测试插入
5、测试更新
3.3、乐观锁
乐观锁:它总是认为不会出现问题,无论干什么都不去上锁;如果出现了问题,就再次更新值测试。
悲观锁:它总是认为会出现问题,无论干什么都会去上锁,再去操作。
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
1、先查询,获取版本号,假设version = 1
-- A线程
update `user` set name = "Jack",version = version + 1
where id = 2 and version = 1
-- B线程抢先完成,这个时候version=2,会导致A修改失败
update `user` set name = "Tom",version = version + 1
where id = 2 and version = 1
测试MybatisPlus中的乐观锁插件
1、给数据库中增加version字段
2、实体类加对应的字段
@Version //乐观锁Version注解
private Integer version;
3、注册组件
@MapperScan("com.kuang.mapper") //扫描mapper文件夹,将MybatisplusApplication中的MapperScan移到此处
@EnableTransactionManagement
@Configuration //配置类
public class MybatisPlusCongif{
//注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
附:
4、测试
测试乐观锁成功(单线程)
@Test
public void testOptimisticLocker(){
//查询用户信息
User user = userMapper.selectById(1L);
//修改用户信息
user.setName("newMan");
user.setEmail("666@qq.com");
//执行更新操作
userMapper.updateById(user);
}
运行结果:
测试乐观锁失败(多线程)
@Test
public void testOptimisticLocker02(){
//线程1
User user = userMapper.selectById(1L);
user.setName("newMan");
user.setEmail("666@qq.com");
//线程2 模拟另外一个线程执行了插队操作
User user2 = userMapper.selectById(1L);
user2.setName("newMan222");
user2.setEmail("222@qq.com");
userMapper.updateById(user2);
//线程1虽然设置了值,但是被线程2插队,导致线程1的执行会失败
userMapper.updateById(user); //如果没有乐观锁,就会覆盖插队线程2的值
}
运行结果:
4、查询操作
4.1、基本查询
//查询一个用户
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
运行结果:
//一次查询多个用户
@Test
public void testSelectById(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
users.forEach(System.out::println);
}
运行结果:
//条件查询map
@Test
public void testSelectByIds(){
HashMap<String,Object> map = new HashMap<>();
//自定义查询
map.put("name","Jack");
map.put("age",20);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
运行结果:
4.2、分页查询
分页插件
@MapperScan("com.kuang.mapper") //扫描mapper文件夹,将MybatisplusApplication中的MapperScan移到此处
@EnableTransactionManagement
@Configuration //配置类
public class MybatisPlusCongif{
//注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
//注册分页
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
测试
@Test
public void testPage(){
//参数一:当前页
//参数二:页面大小
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
System.out.println(page.getTotal());
}
运行结果:
第一页:
第二页:
5、删除操作
5.1、基本删除
//删除一个
@Test
public void testDeleteById(){
userMapper.deleteById(1L);
}
运行结果:
//批量删除
@Test
public void testDeleteBatchIds(){
userMapper.deleteBatchIds(Arrays.asList(2L,3L));
}
//通过map删除
@Test
public void testDeleteMap(){
HashMap<String,Object> map = new HashMap<>();
//自定义查询
map.put("name","Jack");
map.put("age",20);
userMapper.deletaByMap(map);
}
运行结果:
5.2、逻辑删除
物理删除:从数据库中直接移除
逻辑删除:在数据库中没有被移除,而是通过一个变量来让它失效!deleted = 0 ==> deleted = 1
1、在数据表中增加一个字段deleted,默认值为0
2、实体类中增加属性
@TableLogic //逻辑删除注解
private Integer deleted;
3、逻辑删除组件
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
4、配置文件(application.properties)
#配置逻辑删除,默认未删除时deleted的值为0
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
5、执行一个删除操作
记录依旧在数据库,然是deleted值已经发生变化了
现在再次执行查询,发现已经查询不到刚刚删除的用户
6、条件查询器Wapper
测试一
@Test
void test01(){
//查询name不为空的用户,并且邮箱不为空的,年龄大于12
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.ge("age",12);
userMapper.selectList(wrapper).forEach(System.out::println);
}
运行结果:
测试二
@Test
void test02(){
//查询name = Jack的数据
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.eq("name","Jack");
User user = userMapper.selectOne(wrapper); //查询一个数据,出现多个结果使用List或者map
System.out.println(user);
}
运行结果:
测试三
@Test
void test03(){
//查询年龄在20~30岁之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.between("age",10,20); //区间10~20
Integer count = userMapper.selectCount(wrapper); //查询结果数
System.out.println(count);
}
运行结果:
测试四
//模糊查询
@Test
void test04(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.notLike("name","M") //name里不包含“M”
.likeRight("email","1"); //email以“1”开头
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
运行结果:
测试五
@Test
void test05(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//id在子查询中查出来
wrapper
.inSql("id","select id from user where id<3");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
运行结果:
测试六
@Test
void test06(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//通过id进行降序排列
wrapper
.orderByDesc("id");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
运行结果:
注:本笔记来源自学B站up主遇见狂神说后自己整理得来,up主十分良心,疯狂安利!