快速开始
通过sql创建数据库和表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` ( id BIGINT NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT 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');
|
添加依赖
| <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.7</version> </dependency>
|
配置数据库
| server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC spring.datasource.username=root spring.datasource.password=123123 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
在 Spring Boot 启动类中添加 @MapperScan
注解,扫描
Mapper 文件夹:
| @SpringBootApplication @MapperScan("com.zhou.mybatis_plus.mapper") public class MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } }
|
编写实体类 User.java
:
| @Data public class User { private Long id; private String name; private Integer age; private String email; }
|
编写 Mapper 接口类 UserMapper.java
:
| @Repository public interface UserMapper extends BaseMapper<User> { }
|
添加测试类,进行功能测试:
| @SpringBootTest class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper;
@Test void contextLoads() { List<User> users= userMapper.selectList(null);
users.forEach(System.out::println); } }
|
就可以查询到所有user的数据
| User(id=1, name=Jone, age=18, email=test1@baomidou.com) User(id=2, name=Jack, age=20, email=test2@baomidou.com) User(id=3, name=Tom, age=28, email=test3@baomidou.com) User(id=4, name=Sandy, age=21, email=test4@baomidou.com) User(id=5, name=Billie, age=24, email=test5@baomidou.com)
|
配置日志
我们现在所有的sql是不可见的,我们需要知道是什么实现的
配置实现过程在控制台上输出
| mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
|
再次运行 我们就可以得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@244418a] was not registered for synchronization because synchronization is not active 2024-07-12 16:09:17.916 INFO 27212 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2024-07-12 16:09:18.369 INFO 27212 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. JDBC Connection [HikariProxyConnection@1254651534 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e957e2f] will not be managed by Spring ==> Preparing: SELECT id,name,age,email FROM user ==> Parameters: <== Columns: id, name, age, email <== Row: 1, Jone, 18, test1@baomidou.com <== Row: 2, Jack, 20, test2@baomidou.com <== Row: 3, Tom, 28, test3@baomidou.com <== Row: 4, Sandy, 21, test4@baomidou.com <== Row: 5, Billie, 24, test5@baomidou.com <== Total: 5 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@244418a] User(id=1, name=Jone, age=18, email=test1@baomidou.com) User(id=2, name=Jack, age=20, email=test2@baomidou.com) User(id=3, name=Tom, age=28, email=test3@baomidou.com) User(id=4, name=Sandy, age=21, email=test4@baomidou.com) User(id=5, name=Billie, age=24, email=test5@baomidou.com)
|
配置完日志之后,后面的学习就需要注意这个自动生成的SQL
CRUD扩展
插入操作
| @Test public void testInsert() { User user = new User(); user.setName("小周"); user.setAge(18); user.setEmail("123@163.com");
int insert = userMapper.insert(user); System.out.println(user); System.out.println(insert); }
|
| JDBC Connection [HikariProxyConnection@1652552120 wrapping com.mysql.cj.jdbc.ConnectionImpl@62b57479] will not be managed by Spring ==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) ==> Parameters: 1811676431656267777(Long), 小周(String), 18(Integer), 123@163.com(String) <== Updates: 1 User(id=1811676431656267777, name=小周, age=18, email=123@163.com)
|
虽然我们没有设置id的值 但是输出可以看到 已经有id值
所以他是会自动生成的
数据库插入的id的默认值为:全局的唯一id
主键生成策略
雪花算法:
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit是机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),最后还有一个符号位,永远是0。
主键自增:
我们需要配置主键自增
- 实体类字段上 @TableID(type = IdType.AUTO)
- 数据库字段一定要设置为自增
其余源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public enum IdType { AUTO(0), NONE(1), INPUT(2), ASSIGN_ID(3), ASSIGN_UUID(4);
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(); user.setId(5L); user.setName("hhhh");
int i = userMapper.updateById(user); System.out.println(i); }
|
自动填充
创建时间、修改时间!这些个操作一遍都是自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified几乎所有的表都要配置上,而且需要自动化
方式一:数据库级别(工作中不允许)
1、在表中新增字段
create_time、update_time,选择上根据当前时间戳更新
方式二:代码级别
1、删除数据库的默认值、更新操作
2、实体类字段属性上需要
| @Data public class User { 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; }
|
3、编写处理器来处理这个注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler {
@Override public void insertFill(MetaObject metaObject) { log.info("开始插入填充..."); this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); }
@Override public void updateFill(MetaObject metaObject) { log.info("开始更新填充..."); this.setFieldValByName("updateTime", new Date(), metaObject); } }
|
乐观锁
在面试过程中,我们经常会被问到乐观锁,悲观锁
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不去上锁。如果出现了问题,再次更新值测试
悲观锁:顾名思义十分悲观,他总是认为会出现问题,无论干什么都去上锁,再去操作
这里主要讲乐观锁
乐观锁的实现通常包括以下步骤:
- 读取记录时,获取当前的版本号(version)。
- 在更新记录时,将这个版本号一同传递。
- 执行更新操作时,设置
version = newVersion
的条件为
version = oldVersion
。
- 如果版本号不匹配,则更新失败。
| update user set name = "zzk", version = version + 1 where id = 2 and version = 1
update user set name = "zzk", version = version + 1 where id = 2 and version = 1
|
测试MP的乐观锁插件
1、数据库增加字段version
2、实体类加对应字段
| @Data public class User { 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 private Integer version; }
|
3、注册组件
| @Configuration @MapperScan("com.zhou.mybatis_plus.mapper") public class MyBatisPlusConfig {
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }
|
4、测试
| @Test public void testOptimisticLocker2() { User user = userMapper.selectById(1L); user.setName("zzk1"); user.setEmail("1@qq.com"); User user2 = userMapper.selectById(1L); user2.setName("zzk2"); user2.setEmail("2@qq.com"); userMapper.updateById(user2);
userMapper.updateById(user); }
|
查询操作
| @Test public void testSelectByBatchId() { List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); users.forEach(System.out::println); }
@Test public void testSelectByBatchIds() { HashMap<String, Object> map = new HashMap<>(); map.put("name", "小周");
List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
|
分页查询
分页在网站使用的十分多
1、原始的使用limit进行分页
2、pageHelper第三方插件
3、MP也内置了分页插件
如何使用?
1、配置插件
| @Configuration @MapperScan("com.zhou.mybatis_plus.mapper") public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
|
2、测试
| @Test public void testSelectByPage() { Page<User> page = new Page<>(1, 5); userMapper.selectPage(page, null);
page.getRecords().forEach(System.out::println); }
|
逻辑删除
物理删除:从数据库中直接移除
逻辑删除:在数据库中没有被移除
,而是通过一个变量来让他失效,deleted=0 -> deleted=1
管理员可以查看被删除的记录,防止数据的丢失,类似回收站
1、在数据表中增加deleted字段
2、实体类中增加属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Data public class User { 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 private Integer version; @TableLogic private Integer deleted; }
|
3、配置字段
在配置文件中配置
| mybatis-plus.global-config.db-config.logic-delete-field=deleted mybatis-plus.global-config.db-config.logic-delete-value=1 mybatis-plus.global-config.db-config.logic-not-delete-value=0
|
4、测试
| @Test public void testDeleteById() { userMapper.deleteById(1L); }
|
会发现并不是真的删除了 而是将数据库中deleted字段变成了1
走的是更新操作 不是删除操作
性能分析插件
我们在平时的开发中,会遇到一些慢sql
MP提供性能分析插件,如果超过这个时间就停止运行
作用:性能分析拦截器,用于输出每条SQL语句及其执行时间
使用的是p6spy 可以在官网上看https://baomidou.com/guides/p6spy/
条件构造器
十分重要:Wrapper
我们写一些复杂的SQL就可以用它来实现
一些示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| @SpringBootTest public class WrapperTest { @Autowired private UserMapper userMapper;
@Test void contextLoads() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.isNotNull("name") .isNotNull("email") .ge("age", 12); userMapper.selectList(wrapper).forEach(System.out::println); }
@Test void test2() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name", "小周"); User user = userMapper.selectOne(wrapper); System.out.println(user); }
@Test void test3() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age", 20, 30); Long count = userMapper.selectCount(wrapper); System.out.println(count); }
@Test void test4() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.notLike("name", "e").likeRight("email", "t"); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
@Test void test5() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.inSql("id", "select id from user where id<3"); List<Object> objects = userMapper.selectObjs(wrapper); } }
|