文档:https://mp.baomidou.com/
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
创建数据库mybatisplus,并创建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) );
user表插入数据
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');
引入 spring-boot-starter
、spring-boot-starter-test
、mybatis-plus-boot-starter
、h2
依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>Latest Version</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>
说明:我们使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus!
@MapperScan
注解,扫描 Mapper 文件夹:@SpringBootApplication @MapperScan("com.xiaozhi.mapper") public class MybatisplusSpringbootApplication { public static void main(String[] args) { SpringApplication.run(MybatisplusSpringbootApplication.class, args); } }
public class User { private Long id; private String name; private Integer age; private String email; public User() { } public User(Long id, String name, Integer age, String email) { this.id = id; this.name = name; this.age = age; this.email = email; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", email='" + email + '\'' + '}'; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
public interface UserMapper extends BaseMapper<User> { }
# DataSource Config spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatisplus?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: root123
@SpringBootTest class MybatisplusSpringbootApplicationTests { @Autowired private UserMapper userMapper; @Test public void testSelect() { System.out.println(("----- selectAll method test ------")); List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); } }
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
@Test public void testInsert() { User user = new User(); user.setName("xiaozhi"); user.setAge(12); user.setEmail("xiao@xianx.com"); int result = userMapper.insert(user);// 帮我们自动生成id System.out.println(result);// 受影响的行数 System.out.println(user);// 发现: id自动回填 }
数据库插入的id默认值为:全局的唯一id
分布式系统唯一id生成方案:https://www.cnblogs.com/haoxinyue/p/5208136.html
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一!
实体类上增加 @TableId(type = IdType.AUTO)
保证数据库id字段自动递增
public enum IdType { AUTO(0), //数据库id自增 NONE(1), //未设置主键 INPUT(2), //手动输入 ASSIGN_ID(3), ASSIGN_UUID(4); //全局唯一id UUID private final int key; private IdType(int key) { this.key = key; } public int getKey() { return this.key; } }
@Test public void testUpdate(){ User user = new User(5L,"张三",19,"xiao@ss.com"); //注意:updateById 参数是一个对象 int i = userMapper.updateById(user); System.out.println(i); }
创建时间、修改时间!这些个操作都是自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表:gmt_create、gmr_modified、几乎所有的表都要配置上!而且需要自动化!
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
@Component //一定不要忘记把处理器加到IOC容器中 public class MyMetaObjectHandler implements MetaObjectHandler { //插入时候的填充策略 @Override public void insertFill(MetaObject metaObject) { //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) { this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐) } }
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,再次更新值测试! 悲观锁:顾名思义十分悲观,他总是任务总是出现问题,无论干什么都会上锁!再去操作!
乐观锁:1、先查询,获得版本号 version = 1 -- A update user set name = "ChanV", version = version + 1 where id = 2 and version = 1 -- B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败! update user set name = "ChanV", version = version + 1 where id = 2 and version = 1
@Version //乐观锁version注解 private Integer version;
//扫描我们的mapper文件夹 @MapperScan("com.xiaozhi.mapper") @EnableTransactionManagement @Configuration //配置类 public class MyBatisPlusConfig { //注册乐观锁插件 /** * 旧版 */ // @Bean // public OptimisticLockerInterceptor optimisticLockerInterceptor() { // return new OptimisticLockerInterceptor(); // } /** * 新版 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } }
//测试乐观锁成功! @Test public void testOpversion(){ // 查询用户 User user = userMapper.selectById(5L); //修改用户信息 user.setName("小明"); userMapper.updateById(user); } //测试乐观锁失败!多线程下(线程2成功,线程1失败) @Test public void testOpversion1(){ //线程1 // 查询用户 User user = userMapper.selectById(5L); //修改用户信息 user.setName("小明1"); //线程2 User user1 = userMapper.selectById(5L); user1.setName("小明2"); //线程2先提交 int i = userMapper.updateById(user1); System.out.println("线程2update的值为: "+i); //线程1再提交 int i1 = userMapper.updateById(user); System.out.println("线程1update的值为: "+i1); }
@Test public void testSelectByid(){ User user = userMapper.selectById(3L); System.out.println(user); }
@Test public void testSelectByBatchIds(){ List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); users.forEach(System.out::println); }
@Test public void testSelectByMap(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","Xss"); System.out.println(userMapper.selectByMap(map)); }
//分页插件 // 旧版 @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 MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); return interceptor; }
//测试分页查询 @Test public void testPage(){ //参数一:当前页 //参数二:页面大小 //使用了分页插件之后,所有的分页操作也变得简单了! Page<User> page = new Page<>(1, 3); userMapper.selectPage(page, null); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal()); }
基本的删除操作
//测试删除 @Test public void testDeleteById(){ userMapper.deleteById(1L); } //通过id批量删除 @Test public void testDeleteBatchId(){ userMapper.deleteBatchIds(Arrays.asList(2, 3, 4)); } //通过map删除 @Test public void testDeleteMap(){ HashMap<String, Object> map = new HashMap<>(); map.put("name", "陈伟"); userMapper.deleteByMap(map); }
物理删除:从数据库中直接移除 逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效!deleted = 0 => deleted = 1
@TableLogic //逻辑删除 private Integer isdeleted;
mybatis-plus: global-config: db-config: logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
//逻辑删除 @Test public void testIsdelete(){ int i = userMapper.deleteById(1L); System.out.println("执行成功"+i); } @Test public void testIsdelete1(){ int i = userMapper.deleteBatchIds(Arrays.asList(1L,2L,3L)); System.out.println("执行成功"+i); }
执行的是update更新操作
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
查询方式 | 说明 |
---|---|
setSqlSelect | 设置SELECT查询字段 |
where | WHERE语句,拼接 - WHERE条件 |
and | AND语句,拼接 - AND 字段=值 |
andNew | AND语句,拼接 - AND (字段=值) |
or | OR语句,拼接 - OR 字段=值 |
orNew | OR语句,拼接 - OR(字段=值) |
eq | 等于= |
allEq | 基于map内容等于= |
ne | 不等于<> |
gt | 大于> |
ge | 大于等于>= |
lt | 小于< |
le | 小于等于<= |
like | 模糊查询LIKE |
notLike | 模糊查询NOT LIKE |
in | IN查询 |
notin | NOT IN查询 |
isNull | NULL值查询 |
isNotNull | IS NOT NULL |
groupBy | 分组GROUP BY |
having | HAVING关键词 |
orderBy | 排序ORDER BY |
orderAsc | ASC排序ORDER BY |
orderDesc | DESC排序ORDER BY |
exists | EXISTS条件语句 |
notExists | NOT EXISTS条件语句 |
between | BETWEEN条件语句 |
notBetween | NOT BETWEEN条件语句 |
addFilter | 自由拼接SQL |
last | 拼接在最后,例如last("LIMIT 1") |
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取
继承自 AbstractWrapper
,自身的内部属性 entity
也用于生成 where 条件
及 LambdaUpdateWrapper
, 可以通过 new UpdateWrapper().lambda()
方法获取!
//条件构造器 @Test public void testContextloads(){ //查询name不为空的用户,并且邮箱不为空的用户,年龄大于12 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.isNotNull("name") .isNotNull("email") .ge("age",12); List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); } @Test public void testContextloads1(){ //查询名字Sandy QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name","Sandy"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); } @Test public void testContextloads2(){ //查询年龄在10到20岁之间的用户 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.between("age", 10, 20); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); } @Test public void testContextloads3(){ //like模糊查询 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("name","%x%"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); } @Test public void testContextloads4(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //id 在子查询中查出来 //id IN (select id from user where id < 3) wrapper.inSql("id", "select id from user where id < 3"); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); } @Test public void testContextloads5(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //通过id进行排序 wrapper.orderByDesc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
user表
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 createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; @Version //乐观锁version注解 private Integer version; @TableLogic //逻辑删除 private Integer isdeleted; private int r_id; private Role role; }
role表
public class Role { private Long u_id; private String name1; }
mapper-locations: classpath*:/com/xiaozhi/mapper/*.xml
// 在对应的Mapper上面继承基本的接口 BaseMapper @Repository // 代表持久层 public interface UserMapper extends BaseMapper<User> { Page getSelectByid1(Page page); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xiaozhi.mapper.UserMapper"> <select id="getSelectByid1" resultMap="result1"> select * from mybatisplus.user join mybatisplus.role on user.r_id = role.u_id </select> <resultMap id="result1" type="com.xiaozhi.pojo.User"> <result property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> <result property="email" column="email"/> <result property="createTime" column="createTime"/> <result property="updateTime" column="updateTime"/> <result property="version" column="version"/> <result property="isdeleted" column="isdeleted"/> <result property="r_id" column="r_id"/> <association property="role" javaType="com.xiaozhi.pojo.Role"> <result property="u_id" column="u_id"/> <result property="name1" column="name1"/> </association> </resultMap> </mapper>
@Test public void testDou1(){ Page<Object> page = new Page<>(1, 3); userMapper.getSelectByid1(page); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal()); }
参考链接: (2条消息) MyBatis两张表字段名相同产生的问题_Never lose the opportunity to succeed.-CSDN博客