我是 ABin-阿斌:写一生代码,创一世佳话,筑一揽芳华。 如果小伙伴们觉得我的文章有点 feel ,那就点个赞再走哦。
MyBatis:
MyBatis-Plus:
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, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
注意:我们使用 MyBatis-plus 可以节省我们大量的代码,尽量不要同时导入 MyBatis 和 MyBatis-plus 的依赖,以免造成版本冲突。
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.0.5version>
dependency>
# MySQL数据配置
# mysql 5 驱动不同 com.mysql.jdbc.Driver
# mysql 8 驱动不同com.mysql.cj.jdbc.Driver、需要增加时区的配置:serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
Mapper接口
注意:别忘记在主启动类上去扫描我们的mapper包下的所有接口
代码:@MapperScan(“com.mangobin.mapper”)
/**
* @author ABin-阿斌
* @description
* @date 2021/4/1
*/
// 在对应的Mapper上面继承基本的类 BaseMapper
@Repository //该注解表示当前是一个持久层
public interface UserMapper extends BaseMapper<User> {
}
编写测试类
@SpringBootTest
@Slf4j
class MybatisPlus01ApplicationTests {
@Autowired
private UserMapper userMapper;
/**
* 数据查询测试
*/
@Test
void contextLoads() {
//由于我们Mapper 接口继承了 BaseMapper,它里面会帮我们完成最基本的CRUD操作
//而不需要我们去编写 SQL,除非有一些多表或复杂的业务时才需要我们自己去定义
//查询所有用户数据
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
测试结果
当我们执行程序的时候我们可以清晰的看到打印 SQL 语句的日志信息,这个在我们实际开发找 Bug 排错起到了很大的作用。
#日志配置
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
配置完成后的效果展示:
/**
* 数据插入测试
*/
@Test
public void testInsert() {
User user = new User();
user.setName("里西西");
user.setAge(3);
user.setEmail("[email protected]");
int result = userMapper.insert(user);
//打印插入信息,查是否插入成功
log.info("插入的条数为:" + result);
//查看 Id 自增注解是否有效
log.info("Id自增展示:" + user);
}
/**
* 数据更新测试
*/
@Test
public void testUpdate() {
User user = new User();
user.setId(1378265857087610884L);
user.setName("栈北北");
user.setAge(22);
user.setEmail("[email protected]");
int result = userMapper.updateById(user);
//打印插入信息,查是否插入成功
log.info("修改的条数为:" + result);
}
注意事项:
/**
* 根据 Id 查询数据
*/
@Test
public void testSelectById() {
User user = userMapper.selectById(3L);
log.info("查询到的数据为:" + user);
}
/**
* 查询所有数据
*/
@Test
public void testAll() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 3, 5));
users.forEach(System.out::println);
}
/**
* 使用 Map 进行条件查询
*/
@Test
public void testSelectByMap() {
HashMap<String, Object> map = new HashMap(16);
map.put("name", "张三");
map.put("age", 22);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
在实际开发当中不管我们做的是什么项目,分页查询功能是我们不可避免要做的。常见的分页做法有: 原始的 limit 进行分页、pageHelper 第三方插件。当然,在 Mybatis-plus 当中也有它特有的分页功能。
实现思路:
/**
* 分页插件
*
* @return
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@Test
public void testPage() {
/**
* 参数一:当前页
* 参数二:页面大小
*/
Page<User> page = new Page<>(1, 3);
page.getRecords().forEach(System.out::println);
userMapper.selectPage(page, null);
System.out.println(page.getTotal());
}
/**
* 测试删除操作
*/
@Test
public void testDeleteById() {
userMapper.deleteById(4L);
}
/**
* 通过 Id 进行删除
*/
@Test
public void testDeleteBatchIds() {
userMapper.deleteBatchIds(Arrays.asList(
1L,
2L,
6L));
}
/**
* 通过 Map 集合进行删除
*/
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>(16);
map.put("name", "小东东");
map.put("age", 22);
userMapper.deleteByMap(map);
}
什么是逻辑删除
有什么作用
如何使用
/**
* 逻辑删除标识
*/
@TableLogic
private Integer deleted;
/**
* 逻辑删除
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
# 配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
/**
* 测试删除操作
*/
@Test
public void testDeleteById() {
userMapper.deleteById(4L);
}
/**
* 通过 Id 进行删除
*/
@Test
public void testDeleteBatchIds() {
userMapper.deleteBatchIds(Arrays.asList(
1L,
2L,
6L));
}
查询测试
/**
* 查询所有数据
*/
@Test
public void testAll() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 3, 5));
users.forEach(System.out::println);
}
什么是组件生成策略:具体可看这篇文章:什么是组件生成策略
雪花算法:(snowflake)
public enum IdType {
AUTO(0), // 数据库id自增
NONE(1), // 未设置主键
INPUT(2), // 手动输入
ID_WORKER(3), // 默认的全局唯一id
UUID(4), // 全局唯一id uuid
ID_WORKER_STR(5); //ID_WORKER 字符串表示法
}
在我们实际开发当中,一般表中的时间字段是不需要我们手动去做修改,都是由自动化来进行完成。
第二步:在实体类中添加上这两个属性
private Date createTime;
private Date updateTime;
第三步:我们再次测试插入操作,看看是否有变化
第一步:我们去除刚刚在数据库中默认值的配置
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 修改时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
第三步:编写处理器来对该注解进行处理
/**
* @author ABin-阿斌
* @description 时间自动更新处理器
* @date 2021/4/3
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 输入插入时的时间填充
*
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("数据正在插入中.......");
/**
* @param fieldName Java bean属性名称
* @param fieldVal java bean 属性值
* @param metaObject 元对象参数
*/
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
/**
* 数据修改时的数据填充
*
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("数据正在修改中......");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
第四步:测试我们的插入与更新,查看是否生效
具体的实现思路:
乐观锁:1、先查询,获得版本号 version = 1
-- A
update user set name = "张三", version = version + 1
where id = 2 and version = 1
-- B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败!
update user set name = "张三", version = version + 1
where id = 2 and version = 1
第一步:我们在数据库中添加 version(版本号)字段
/**
* 版本号:乐观锁
*/
@Version
private Integer version;
第三步:注入乐观锁的插件
/**
* @author ABin-阿斌
* @description Mybatis-Plus配置类
* @date 2021/4/3
*/
@MapperScan("com.mangobin.mapper")
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
/**
* 注入乐观锁插件
*
* @return
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
第四步:编写乐观锁测试类
/**
* 测试乐观锁的版本号机制是否生效
*/
@Test
public void testOptimisticLocker() {
User user = userMapper.selectById(3L);
user.setName("李四");
user.setAge(22);
userMapper.updateById(user);
}
/**
* 乐观锁失效:多线程并发下的修改操作
*/
@Test
public void testConcurrentModification() {
User user = userMapper.selectById(3L);
user.setName("马到成功");
user.setAge(22);
User user2 = userMapper.selectById(3L);
user2.setAge(25);
user2.setName("青云直上");
userMapper.updateById(user2);
userMapper.updateById(user);
}
在我们日常开发工作当中,避免不了查看当前程序所执行的 SQL 语句,便于程序员排忧解难。
MyBatis-plus 提供了两种方式,用于输出每条 SQL 语句及其执行时间,针对执行较长时间的 SQL 可以停止运行,有助于发现问题。
注意: 这两种方式只适用于开发环境和测试环境,不建议生产环境使用,因为在分析SQL时会增大我们的一个性能消耗。
/**
* SQL执行效率插件
* @Profile设置: dev test 环境开启,保证我们的效率
*/
@Bean
@Profile({"dev", "test"})
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1000);
// ms设置sql执行的最大时间,如果超过了则不 执行
performanceInterceptor.setFormat(true);
// 是否格式化代码
return performanceInterceptor;
}
# 配置开发环境:dev
spring.profiles.active=dev
/**
* 数据查询测试
*/
@Test
void contextLoads() {
//由于我们Mapper 接口继承了 BaseMapper,它里面会帮我们完成最基本的CRUD操作
//而不需要我们去编写 SQL,除非有一些多表或复杂的业务时才需要我们自己去定义
//查询所有用户数据
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
package com.mangobin;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mangobin.entity.User;
import com.mangobin.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @author ABin-阿斌
* @description
* @date 2021/4/5
*/
@SpringBootTest
@Slf4j
public class WrapperTest {
@Autowired
private UserMapper userMapper;
/**
* isNotNull(不为空)、ge(大于等于)、 eq(等于) 的具体使用
* 查询用户名、邮箱不为空的用户,且年龄 >= 12的
*/
@Test
void contextLoads() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
// userQueryWrapper.isNotNull("name")
// .isNotNull("age")
// .ge("age", 12);
// List users = userMapper.selectList(userQueryWrapper);
// users.forEach(System.out::println);
userQueryWrapper.eq("name", "青云直上");
userMapper.selectOne(userQueryWrapper);
System.out.println(userQueryWrapper);
}
/**
* Between(区间范围)、Like(模糊查询:左右)、OrderBy(排序)
*/
@Test
void test02() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//区间条件
// userQueryWrapper.between("age", 20, 30);
// userMapper.selectCount(userQueryWrapper);
//模糊查询
/* userQueryWrapper.notLike("name", "e")
.likeRight("email", "t");
List
//通过子查询进行查询
userQueryWrapper.inSql("id", "select id from user where id <= 3");
List<User> users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
//排序
//通过 Id 进行升序查询
// userQueryWrapper.orderByAsc("id");
// List users = userMapper.selectList(userQueryWrapper);
// users.forEach(System.out::println);
}
}