简化Mybatis MyBatisPlus官网
本笔记是基于【狂神说Java】MyBatisPlus最新完整教程通俗易懂
MySQL版本 5.7(5.7以下的更新一下,不然后续学习自动填充会卡壳) 添加createTime和updateTime导致导入 SQL 时出现 Invalid default value for ‘create_time’ 报错解决方法
MyBatis-Plus版本 3.0.5
mybatisplus
数据库<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<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>
dependencies>
# mysql 5 驱动不同 com.mysql.jdbc.Driver
# mysql 8 com.mysql.cj.jdbc.Driver 需要增加时区配置
spring.datasource.username=root
spring.datasource.password=250259
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
UserMapper
@Repository // 代表持久层
public interface UserMapper extends BaseMapper<User> {
// 所有CRUD文件已经生成
}
// 扫描mapper文件夹
@MapperScan("com.example.mapper")
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
System.out::println是什么?
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
// 参数是一个mapper,条件构造器
// 查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
图1 执行结果
我们所有的sql现在都是不可见的,我们希望知道它是怎么执行的,所以我们必须要看日志
# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
图2 日志输出SQL语句
@Test
public void testInsert() {
User user = new User();
user.setName("ccy");
user.setAge(20);
user.setEmail("[email protected]");
int result = userMapper.insert(user); // 帮我们自动生成ID
System.out.println(result); // 受影响的行数
System.out.println(user); // ID会自动回填
}
图3 Insert自动生成ID并且回显在数据库中
不难发现,我们在没有设置ID的情况下,自动生成了ID并且回显在了数据库里,这是基于mybatis-plus中基于主键ID的主键生成
分布式唯一ID生成
数据库插入ID的默认值为:全局的唯一ID
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
// 对应数据库中的主键(uuid,自增id,雪花算法)
// AUTO(0), 数据库ID自增,切记要设计数据库主键ID自增
// NONE(1), 未设置主键
// INPUT(2), 手动输入
// ID_WORKER(3), 默认为全局唯一ID
// UUID(4), 全局唯一ID
// ID_WORKER_STR(5); ID_WORKER字符串表示法
@TableId(type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
}
只改变ID为5L的姓名
@Test
public void testUpdate() {
User user = new User();
user.setId(5L);
user.setName("ccy");
int result = userMapper.updateById(user);
System.out.println(result);
}
图4 Update操作
增加改变年龄
@Test public void testUpdate() { User user = new User(); user.setId(5L);// 根据条件自动生成动态sql user.setName("ccy"); user.setAge(21); int result = userMapper.updateById(user); System.out.println(result); }
图5 与图4做对比动态生成SQL
我们会发现,mybatis-plus会帮我们自动生成动态sql
方式一:数据库级别(工作中不允许修改数据库)
图6 修改数据库
@Data@NoArgsConstructor@AllArgsConstructorpublic class User { @TableId(type = IdType.ID_WORKER) private Long id; private String name; private Integer age; private String email; private Date createTime; private Date updateTime;}
图7 更新时间变化
方式二:代码级别
图8 修改数据库
// 字段添加填充内容 @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
@Component // 将注解处理器加入到IOC容器中
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill");
// 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) {
log.info("start update fill");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
图9 执行Insert操作后数据库变化
图10 执行Update操作更新时间变化
乐观锁和悲观锁
MyBatis-Plus——乐观锁
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
图11 修改数据库
@Version // 乐观锁version注解private Integer version;
// 扫描mapper文件夹,有了配置类以后就可以将启动类中的扫描移动到这里@MapperScan("com.example.mapper")@EnableTransactionManagement@Configurationpublic class MyBatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }}
// 测试乐观锁 @Test public void testOptimisticLocker() {// 查询用户信息 User user = userMapper.selectById(4L);// 修改用户信息 user.setName("shuang"); user.setEmail("[email protected]");// 执行更新操作 userMapper.updateById(user); }
图12 SQL语句中判断条件新增version
// 测试多线程乐观锁
@Test
public void testOptimisticLocker2() {
// 线程1
User user = userMapper.selectById(3L);
user.setName("fs");
user.setEmail("[email protected]");
// 模拟另外一个线程执行了插队操作
User user2 = userMapper.selectById(3L);
user2.setName("ccy丶ccy");
user.setEmail("[email protected]");
userMapper.updateById(user2);
// 自旋锁来多次尝试操作!
userMapper.updateById(user); // 如果没有乐观锁就会覆盖插队线程的值
}
图13 乐观锁加持下version变化
由于乐观锁的机制,version并没有变成3
// 测试单一查询
@Test
public void testSelectById() {
System.out.println(userMapper.selectById(1L));
}
图14 单一查询
// 测试批量查询@Testpublic void testSelectBatchIds(){ ArrayList list = new ArrayList<>(); list.add(1L); list.add(2L); list.add(3L); List users = userMapper.selectBatchIds(list); users.forEach(System.out::println);}
图15 批量查询
图16 条件查询
// 条件查询@Testpublic void test() { HashMap map = new HashMap<>(); map.put("name", "ccy"); map.put("age", 21); List users = userMapper.selectByMap(map); users.forEach(System.out::println);}
// 分页插件@Beanpublic PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor();}
// 分页查询 @Test public void testPage() {// 参数一:当前页// 参数二:页面大小 Page page = new Page<>(1, 3); userMapper.selectPage(page, null); page.getRecords().forEach(System.out::println);// 获得分页总数 System.out.println(page.getTotal()); }
图17 分页查询
@Testpublic void testDelete() { userMapper.deleteBatchIds(Arrays.asList(5L,6L));}
图18 其他的一些方法就不再赘述了基本同查询操作类似
物理删除:从数据库中直接移除
逻辑删除:从数据库中没有被移除,而是通过一个变量deleted让它失效
图19 修改数据库
@TableLogicprivate Integer deleted;
#配置逻辑删除mybatis-plus.global-config.db-config.logic-delete-value=1mybatis-plus.global-config.db-config.logic-not-delete-value=0
// 逻辑删除组件@Beanpublic ISqlInjector sqlInjector() { return new LogicSqlInjector();}
图20 逻辑删除结果
图21 执行Select同之前的改变
# 设置开发环境
spring.profiles.active=dev
// SQL执行效率插件
@Bean
@Profile({"dev", "test"})// 设置dev test环境开启,保证我们的效率
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1); // 设置sql的执行的最大时间,如果超过了则不执行,单位毫秒
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
图22 SQL执行超时
performanceInterceptor.setMaxTime(100)
图23 成功执行
Wrapper是遵循链式编程的
图24 wrapper方法
测试
@SpringBootTest
class WrapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testGe() {
// 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("email")
.ge("age", 12);
userMapper.selectList(wrapper).forEach(System.out::println);
}
@Test
void testEq() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "shuang");
// 查询一个的时候使用selectOne,查询多个的时候使用map,list
System.out.println(userMapper.selectOne(wrapper));
}
@Test
void testBetween() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30);
System.out.println(userMapper.selectCount(wrapper));
}
@Test
void testLike() {
// 模糊查询
// likeRight,likeLeft
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name","ccy")
.likeRight("email",1);
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
@Test
void testInSql() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 子查询
wrapper.inSql("id","select id from user where id < 3");
List<Object> objs = userMapper.selectObjs(wrapper);
objs.forEach(System.out::println);
}
@Test
void test() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 降序查询
wrapper.orderByDesc("id");
List<User> list = userMapper.selectList(wrapper);
list.forEach(System.out::println);
}
}
public class KuangCode {
public static void main(String[] args) {
// 需要构建一个 代码自动生成器对象
AutoGenerator mpg = new AutoGenerator();
// 配置策略
// 1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("狂神说");
gc.setOpen(false);
gc.setFileOverride(false);
// 是否覆盖
gc.setServiceName("%sService");
// 去Service的I前缀
gc.setIdType(IdType.ID_WORKER);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//2、设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/kuang_community? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、包的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("blog");
pc.setParent("com.kuang");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
//4、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("blog_tags", "course", "links", "sys_settings", "user_record", " user_say");
// 设置要映射的表名
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
// 自动lombok;
strategy.setLogicDeleteFieldName("deleted");
// 自动填充配置
TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 乐观锁
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true);
// localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute();
//执行
}
}
图25 代码生成器效果图