MP入门
对mybatis
增求,简化开发
-
创建数据库和表,添加数据
create database mybatis_plus; user mybatis_plus; 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) ); 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');
-
创建
SpringBoot
工程 -
引入
mybatis-plus
相关依赖<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
-
配置数据库对应的属性
# mysql8+ 用com.mysql.cj.jdbc.Driver 和 serverTimezone=GMT%2B8时区 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=
-
编写实体类
package com.jerry.mpdemo.pojo; import lombok.Data; @Data public class User { private Long id; private String name; private Integer age; protected String email; }
-
编写
Mapper
文件package com.jerry.mpdemo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.jerry.mpdemo.pojo.User; public interface UserMapper extends BaseMapper<User> { }
-
扫描
mapper
@SpringBootApplication @MapperScan("com.jerry.mpdemo.mapper") public class MpdemoApplication { public static void main(String[] args) { SpringApplication.run(MpdemoApplication.class, args); } }
-
测试
@SpringBootTest class MpdemoApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads() { List<User> users = userMapper.selectList(null); users.forEach((User user) -> { System.out.println(user); }); } }
主键
MP自动生成19位的ID
自动增长
@Data
public class User {
@TableId(type = IdType.AUTO) // 加注解
private Long id;
private String name;
private Integer age;
protected String email;
}
更新
@Test
public void updateUser() {
User user = new User();
user.setId(2L);
user.setAge(21);
int row = userMapper.updateById(user);
System.out.println(row);
}
自动填充
-
表添加两个字段
create_time
update_time
-
实体类添加属性
private Date createTime; private Date updateTime;
-
添加注解
@TableField(fill = FieldFill.INSERT)
-
实现接口
MetaObjectHandler
@Component // 放在spring容器中,很重要!! public class MyMetaObjectHandler implements MetaObjectHandler { // 使用mp实现更新操作,这个方法执行 @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", new Date(), metaObject); } // 使用mp实现添加操作,这个方法执行 @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } }
-
正常添加更新即可,不用再写这两个字段
乐观锁
主要解决丢失更新,如果不考虑事务隔离性,产生读问题:
- 脏读
- 不可重复读
- 幻读s
写问题:丢失更新问题:
比如id = 1
salary = 500
,两个人都想修改这个工资
两人都需要开启事务,A
将500
改为了800
,B
将500
改成200
事务最终需要提交,如果A
先提交了事务,表中工资就会变为800
,
B
随后提交事务,工资变为了200
,此时A
看到了就会发现不对劲,
自己提交的数据被覆盖了。正常应该是B
把800
改成200
解决方法:
-
悲观锁:串行,
A
用的时候别人不能用,隔离级别max -
乐观锁:获取当前version,
A
提交时比较版本号和数据库中的是否一样,然后将版本号+1,B
在看到版本号不一样就无法提交 -
添加字段,作为版本号
-
实体类添加版本号
private Integer version;
-
添加
@Version
注解@Version private Integer version;
-
配置乐观锁的插件
@Configuration @MapperScan("com.jerry.mpdemo.mapper") public class MpConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
-
测试
@Test public void addUser() { User user = new User(); user.setAge(18); user.setName("xxx"); user.setEmail("123456321@369.com"); int insert = userMapper.insert(user); System.out.println("insert: ---------- " + insert); } // 乐观锁必须先查再改 @Test public void testLock() { User user = userMapper.selectById(1355352835969359878L); user.setAge(666); int row = userMapper.updateById(user); System.out.println(row); }
查询
多个id
的批量查询
@Test
public void testSelect() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); // 将1,2,3查询出来
for (User user : users) {
System.out.println(user);
}
}
分页查询:
-
配置分页插件
@Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
-
使用
@Test public void testPage() { Page<User> page = new Page<>(1, 3); // 当前页, 每页记录数 IPage<User> userIPage = userMapper.selectPage(page, null); System.out.println(userIPage.getCurrent()); System.out.println(userIPage.getSize()); System.out.println(userIPage.getTotal()); System.out.println(userIPage.getPages()); List<User> records = userIPage.getRecords(); for (User record : records) { System.out.println(record); } }
删除
物理删除
@Test
public void testDelete() {
int count = userMapper.deleteById(1L);
System.out.println("成功删除了" + count + "行");
}
批量删除
@Test
public void testBatchDelete() {
userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
}
逻辑删除,数据真实存在,但是查询不出来
-
添加deleted字段
-
添加字段并且添加注解
@TableLogic
-
配置逻辑删除插件
@Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); }
性能分析
@Bean
@Profile({"dev", "test"})
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(200); // 超过100ms的sql不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
条件查询
@Test
public void testSelectQuery() {
QueryWrapper queryWrapper = new QueryWrapper();
// ge大于等于 gt大于 le小于等于 lt小于
queryWrapper.ge("age", 20);
List<User> list = userMapper.selectList(queryWrapper);
list.forEach((user) -> System.out.println(user));
// eq等于 ne不等于
// between在范围内 like模糊查询会->自动帮忙传递%
// orderBy排序
// last拼接到sql的最后,有sql注入风险
// 查询指定列
queryWrapper.select("id", "name", "age");
}