mybatis-plus应用
官网地址[链接](https://mp.baomidou.com/)
版本: 3.0.5
导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ccj.appliction</groupId>
<artifactId>appliction</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>appliction</name>
<description>firstAppliction</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<druid.version>1.0.26</druid.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.8</version>
</dependency>
<!-- Mybatis-plus -->
<!-- mybatisPlus 核心库 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
连接数据库
spring:
#redis简单配置
redis:
host: 127.0.0.1
port: 6379
database: 0
#数据源
datasource:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/appliction?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
filters: stat,wall
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis-plus:
mapper-locations: classpath:/mappers/*Mapper.xml
typeAliasesPackage: com.ccj.application
global-config:
id-type: 0
field-strategy: 1
#db-column-underline: true
refresh-mapper: true
#capital-mode: true
#key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
#logic-delete-value: 1
#logic-not-delete-value: 0
#sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
#meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
#\u914D\u7F6EJdbcTypeForNull
jdbc-type-for-null: 'null'
#日志输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
pojo-dao(连接mybatis,配置mapper.xml文件)-serice-controller
使用mybatis-plus之后
-
pojo
package com.ccj.application.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * <p> * 用户表 * </p> * * @author Cai.ChangJun * @since 2020-07-12 */ @Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private static final long serialVersionUID = 1L; private Long id; private String name; private Integer age; private String email; }
-
mapper接口
package com.ccj.application.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ccj.application.model.User; import org.springframework.stereotype.Repository; /** * <p> * 用户表 Mapper 接口 * </p> * * @author Cai.ChangJun * @since 2020-07-12 */ @Repository public interface UserMapper extends BaseMapper<User> { }
baseMapper源码直观感受
package com.baomidou.mybatisplus.core.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.Param; public interface BaseMapper<T> { int insert(T var1); int deleteById(Serializable var1); int deleteByMap(@Param("cm") Map<String, Object> var1); int delete(@Param("ew") Wrapper<T> var1); int deleteBatchIds(@Param("coll") Collection<? extends Serializable> var1); int updateById(@Param("et") T var1); int update(@Param("et") T var1, @Param("ew") Wrapper<T> var2); T selectById(Serializable var1); List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> var1); List<T> selectByMap(@Param("cm") Map<String, Object> var1); T selectOne(@Param("ew") Wrapper<T> var1); Integer selectCount(@Param("ew") Wrapper<T> var1); List<T> selectList(@Param("ew") Wrapper<T> var1); List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> var1); List<Object> selectObjs(@Param("ew") Wrapper<T> var1); IPage<T> selectPage(IPage<T> var1, @Param("ew") Wrapper<T> var2); IPage<Map<String, Object>> selectMapsPage(IPage<T> var1, @Param("ew") Wrapper<T> var2); }
-
注意点:在appliction.java主启动类加上@MapperScan(mapper位置)
-
使用
package com.ccj.application; import com.ccj.application.model.User; import com.ccj.application.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest public class SpringbootMybatisPlusApplicationTests { @Autowired private UserService userService; @Test public void contextLoads() { // 所有用户 List<User> users = userService.selectList(null); System.out.println(users); //查询id=1的用户 User user = userService.selectById(1); System.out.println("users1=="+user); } }
分布式系统唯一id生成:https://blog.csdn.net/rainyear/article/details/86293122
Twitter的snowflake算法
雪花算法:
nowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。
算法实现:
public class IdWorker
{
private long workerId;
private long datacenterId;
private long sequence = 0L;
private static long twepoch = 1288834974657L;
private static long workerIdBits = 5L;
private static long datacenterIdBits = 5L;
private static long maxWorkerId = -1L ^ (-1L << (int)workerIdBits);
private static long maxDatacenterId = -1L ^ (-1L << (int)datacenterIdBits);
private static long sequenceBits = 12L;
private long workerIdShift = sequenceBits;
private long datacenterIdShift = sequenceBits + workerIdBits;
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = -1L ^ (-1L << (int)sequenceBits);
private long lastTimestamp = -1L;
private static object syncRoot = new object();
public IdWorker(long workerId, long datacenterId)
{
// sanity check for workerId
if (workerId > maxWorkerId || workerId < 0)
{
throw new ArgumentException(string.Format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0)
{
throw new ArgumentException(string.Format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public long nextId()
{
lock (syncRoot)
{
long timestamp = timeGen();
if (timestamp < lastTimestamp)
{
throw new ApplicationException(string.Format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp)
{
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0)
{
timestamp = tilNextMillis(lastTimestamp);
}
}
else
{
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << (int)timestampLeftShift) | (datacenterId << (int)datacenterIdShift) | (workerId << (int)workerIdShift) | sequence;
}
}
protected long tilNextMillis(long lastTimestamp)
{
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
}
mybatis-plus中的id自增策略
@TableId(type = "")
type种类:
AUTO(0), //数据库id自增
NONE(1),//未设置主键
INPUT(2),//手动输入
ID_WORKER(3),//默认全局唯一id
UUID(4),//全局唯一id uuid
ID_WORKER_STR(5); //ID_WORKER的字符串表示发
插入操作
自增策略
需要配置主键自增
-
实体类上@TableId(type = IdType.AUTO)
-
数据库字段要是自增
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRdJN1XQ-1612421886576)(C:\Users\ccj\AppData\Roaming\Typora\typora-user-images\image-20200718232935065.png)]
@Test public void testinsert(){ User user = new User(); user.setName("张三"); user.setAge(23); user.setEmail("123456@qq.com"); Integer insert = userMapper.insert(user); System.out.println(insert); System.out.println(user); }
更新操作
@Test
public void testUpdate(){
User user = new User();
user.setId(6L);
user.setName("李四");
user.setAge(23);
//null和""都不更新该字段
user.setEmail("123456@qq.com");
userMapper.updateById(user);
}
自动填充
创建时间、修改时间。。。这些操作都是数据库自动完成的,我们不希望手动更新
阿里巴巴开发手册: 参考阿里规范 建表约规 第9条
1. 【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint
(1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在<resultMap>设置从 is_xxx 到
Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含
义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
2. 【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只
出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、表名、
字段名,都不允许出现任何大写字母,避免节外生枝。
正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name
3. 【强制】表名不使用复数名词。
说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数形式,符合
表达习惯。
4. 【强制】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。
5. 【强制】主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。
6. 【强制】小数类型为 decimal,禁止使用 float 和 double。
说明:在存储的时候,float 和 double 都存在精度损失的问题,很可能在比较值的时候,得到不正确的
结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数并分开存储。
7. 【强制】如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
8. 【强制】varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度
大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效
率。
9. 【强制】表必备三字段:id, gmt_create, gmt_modified。
说明:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。gmt_create, gmt_modified
的类型均为 datetime 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。
Java 开发手册
37/57
10.【推荐】表的命名最好是遵循“业务名称_表的作用”。
正例:alipay_task / force_project / trade_config
11.【推荐】库名与应用名称尽量一致。
12.【推荐】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
13.【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1) 不是频繁修改的字段。
2) 不是唯一索引的字段。
3) 不是 varchar 超长字段,更不能是 text 字段。
正例:各业务线经常冗余存储商品名称,避免查询时需要调用 IC 服务获取。
14.【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
15.【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索
速度。
正例:无符号值可以避免误存负数,且扩大了表示范围。
对象 年龄区间 类型 字节 表示范围
人 150 岁之内 tinyint unsigned 1 无符号值:0 到 255
龟 数百岁 smallint unsigned 2 无符号值:0 到 65535
恐龙化石 数千万年 int unsigned 4 无符号值:0 到约 43 亿
太阳 约 50 亿年 bigint unsigned 8 无符号值:0 到约 10 的 19 次方
方式一:数据库级别
-
在表中新增字段create_time,update_time设置默认值
DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APHZZFTg-1612421886578)(C:\Users\ccj\AppData\Roaming\Typora\typora-user-images\image-20200719001211235.png)]
插入数据时会自动插入时间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGuGvOYc-1612421886579)(C:\Users\ccj\AppData\Roaming\Typora\typora-user-images\image-20200719002458007.png)]
方式二:代码级别
-
数据库中没有自动生成日期的默认值
-
实体类上需要怎加注解
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
-
编写处理器处理注解
package com.ccj.application.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.Date; /** * @Description: 原数据处理 * @Date: 2020/7/19 * @author:Cai.ChangJun */ @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入时的填充策略 * @Author Cai.ChangJun * @param metaObject : * @return void * @throws * @Date 2020/7/19 0:51 */ @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill ...."); this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); this.setInsertFieldValByName("updateTime", new Date(), metaObject); } /** * 更新时的填充策略 * @Author Cai.ChangJun * @param metaObject : * @return void * @throws * @Date 2020/7/19 0:52 */ @Override public void updateFill(MetaObject metaObject) { log.info("start update fill ...."); this.setInsertFieldValByName("updateTime",new Date(), metaObject); } }
乐观锁
乐观锁,悲观锁
原子引用
乐观锁: 总是认为不会出现问题,无论做什么都不去上锁,如果出现问题,再次更新值测试
悲观锁:总是认为会出现问题,无论做什么都会上锁,再去操作
version、new version
乐观锁:1、先查询、获取版本号 version = 1
---线程A
update user set name = "caichangjun", version = version + 1
where id = 2 and version = 1
---线程B 抢先完成
update user set name = "caichangjun", version = version + 1
where id = 2 and version = 1
mybatis-plus实现乐观锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6gud4Fpt-1612421886581)(C:\Users\ccj\AppData\Roaming\Typora\typora-user-images\image-20200719012001888.png)]
@Version
private Integer version;
-
注册组件
在mybatisConfig配置类中添加乐观锁插件
package com.ccj.application.config; import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * @author zh * @ClassName cn.saytime.config.MybatisConfiguration * @Description */ //开启事务 @EnableTransactionManagement @Configuration @MapperScan("com.ccj.application.dao") public class MybatisConfiguration { /* * 分页插件,自动识别数据库类型 * 多租户,请参考官网【插件扩展】 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } /** * mybatis-plus实现乐观锁 注册乐观锁插件 * @Author Cai.ChangJun * @return com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor * @throws * @Date 2020/7/19 1:22 */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } /* * oracle数据库配置JdbcTypeForNull * 参考:https://gitee.com/baomidou/mybatisplus-boot-starter/issues/IHS8X 不需要这样配置了,参考 yml: mybatis-plus: confuguration dbc-type-for-null: 'null' @Bean public ConfigurationCustomizer configurationCustomizer(){ return new MybatisPlusCustomizers(); } class MybatisPlusCustomizers implements ConfigurationCustomizer { @Override public void customize(org.apache.ibatis.session.Configuration configuration) { configuration.setJdbcTypeForNull(JdbcType.NULL); } } */ }
//测试乐观锁成功 @Test public void testOptimisticLockerInterceptor(){ //查询用户信息 User user = userMapper.selectById(1); //修改用户信息 user.setName("caichangjun"); user.setEmail("123456@qq.com"); //执行更新操作 userMapper.updateById(user); } //测试乐观锁失败 @Test public void testOptimisticLockerInterceptorError(){ //线程1 //查询用户信息 User user = userMapper.selectById(1); //修改用户信息 user.setName("caichangjun"); user.setEmail("123456@qq.com"); //模拟另一个线程执行插队操作 User user2 = userMapper.selectById(1); user2.setName("caichangjun2222"); user2.setEmail("222@qq.com"); userMapper.updateById(user2); //自旋锁来多次尝试提交 userMapper.updateById(user); //如果没有乐观锁就会覆盖插队线程的值 }
查询操作
//测试批量查询
@Test
public void testSelect(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
System.out.println(users);
}
@Test
//条件查询使用map
public void testSelectByMap(){
Map<String, Object> map = new HashMap<>();
map.put("name","张三");
userMapper.selectByMap(map);
}
分页查询
-
原始limit分页
-
pageHelper 第三方插件
-
mybatis-plus内置插件
使用mybatis-plus插件实现分页
1.配置拦截器组件
//Spring boot方式 @EnableTransactionManagement @Configuration @MapperScan("com.baomidou.cloud.service.*.mapper*") public class MybatisPlusConfig { @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; } }
-
测试
@Test //测试分页查询 public void testPage(){ //参数一:当前页 //参数二:页面大小 Page<User> userPage = new Page<>(1,5); IPage<User> userIPage = userMapper.selectPage(userPage, null); userIPage.getRecords().forEach(System.out::println); }
-
基本的删除操作
-
根据id删除数据
@Test //测试删除 public void testDelete(){ int i = userMapper.deleteById(1284534058264678402L); System.out.println(i); } @Test //通过id批量删除 public void testDeleteBatchId(){ userMapper.deleteBatchIds(Arrays.asList(2, 3, 4)); } @Test //根据条件删除 public void testDeleteByMap(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","李四"); userMapper.deleteByMap(map); }
逻辑删除
物理删除: 在数据库中直接移除
逻辑删除: 在数据库中没有被移除,而是通过一个变量来让他失效 deleted = 0 => deleted = 1
测试:
-
在数据库表中添加一个delete字段
-
实体类中增加属性
@TableLogic //逻辑删除 private Integer deleted;
-
配置逻辑删除
/**
* 逻辑删除组件
* @Author Cai.ChangJun
* @return com.baomidou.mybatisplus.core.injector.ISqlInjector
* @throws
* @Date 2020/7/19 17:10
*/
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
性能分析
查询数据库中执行的慢sql
mybatis-plus提供的性能分析插件,如果超过这个时间就停止运行,作用:性能分析拦截器,用于输出每条sql语句及其执行时间
-
导入插件
设置springboot环境为测试环境
spring: #设置开发环境 profiles: active: dev
/** * SQL执行效率插件 */ @Bean @Profile({"dev","test"})// 设置 dev test 环境开启 public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); //ms 设置sql执行最大时间超过了不执行 performanceInterceptor.setMaxTime(100); //开启格式化支持 performanceInterceptor.setFormat(true); return performanceInterceptor; }
-
测试使用
Time:8 ms - ID:com.ccj.application.dao.UserMapper.selectPage
Execute SQL:
SELECT
id,
name,
age,
email,
create_time,
update_time,
version,
is_delete
FROM
user
WHERE
is_delete=0 LIMIT 0,5
条件构造器
复杂sql可以使用它替代
条件参数说明
查询方式 | 说明 |
---|---|
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”) |
package com.ccj.application;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ccj.application.dao.UserMapper;
import com.ccj.application.model.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
/**
* @Description: 条件构造器测试
* @Date: 2020/7/19
* @author:Cai.ChangJun
*/
@SpringBootTest
public class WrapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void contextLoads() {
//查询name不为空的并且邮箱不为空的,年龄大于等于12
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,25);
Integer integer = userMapper.selectCount(wrapper);
System.out.println(integer);
}
//模糊查询
@Test
void test4(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name","三")
.likeRight("email", "9");
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
//模糊查询
@Test
void test5(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//id在子查询中查询出来
wrapper.inSql("id","select id from user where id < 3");
/*SELECT
id,
name,
age,
email,
create_time,
update_time,
version,
is_delete
FROM
user
WHERE
is_delete=0
AND id IN (
select
id
from
user
where
id < 3
)*/
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
//查询排序
@Test
void test6(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//通过id进行降序排序
wrapper.orderByDesc("id");
userMapper.selectList(wrapper).forEach(System.out::println);
}
}
代码自动生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
测试
加入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.1.8.RELEASE</version>
</dependency>
代码实现
package com.ccj.application;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
/**
* @Description: 代码生成器
* @Date: 2020/7/19
* @author:Cai.ChangJun
*/
public class AutoCode {
public static void main(String[] args) {
//需要构建一个代码生成器对象
AutoGenerator autoGenerator = new AutoGenerator();
//配置策略
//1.全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath+"/src/main/java");
//生成作者
gc.setAuthor("Cai.ChangJun");
//是否打开资源管理器
gc.setOpen(false);
//是否覆盖原来生成的文件
gc.setFileOverride(true);
//去service的I前缀
gc.setServiceName("%sService");
//设置生成id策略 雪花算法
gc.setIdType(IdType.ID_WORKER);
//设置日期类型
gc.setDateType(DateType.ONLY_DATE);
//自动生成swagger文档
// gc.setSwagger2(true);
//将配置文件放入代码生成器对象中
autoGenerator.setGlobalConfig(gc);
//设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://192.168.71.10:3306/appliction?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
autoGenerator.setDataSource(dsc);
//3.包的配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.ccj.application");
pc.setEntity("model");
pc.setMapper("dao");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("api");
autoGenerator.setPackageInfo(pc);
//4.策略配置
StrategyConfig strategy = new StrategyConfig();
//设置要映射的表名
strategy.setInclude("test");
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
//逻辑删除
strategy.setLogicDeleteFieldName("is_delete");
//乐观锁
strategy.setVersionFieldName("version");
//自动填充
TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(createTime);
tableFills.add(updateTime);
strategy.setTableFillList(tableFills);
//设置restful风格的controller
strategy.setRestControllerStyle(true);
//127.0.0.1:8080/hello_id_2
strategy.setControllerMappingHyphenStyle(true);
/* // 公共父类
strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");*/
autoGenerator.setStrategy(strategy);
/* // 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
autoGenerator.setTemplate(templateConfig);*/
autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
//执行
autoGenerator.execute();
}
}
原学习视频地址
https://www.bilibili.com/video/BV17E411N7KN?p=1
原视频作者:
te_time", FieldFill.INSERT);
TableFill updateTime = new TableFill(“update_time”, FieldFill.INSERT_UPDATE);
ArrayList tableFills = new ArrayList<>();
tableFills.add(createTime);
tableFills.add(updateTime);
strategy.setTableFillList(tableFills);
//设置restful风格的controller
strategy.setRestControllerStyle(true);
//127.0.0.1:8080/hello_id_2
strategy.setControllerMappingHyphenStyle(true);
/* // 公共父类
strategy.setSuperControllerClass(“你自己的父类控制器,没有就不用设置!”);
// 写于父类中的公共字段
strategy.setSuperEntityColumns(“id”);
strategy.setInclude(scanner(“表名,多个英文逗号分割”).split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + “_”);/
autoGenerator.setStrategy(strategy);
/ // 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
autoGenerator.setTemplate(templateConfig);*/
autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
//执行
autoGenerator.execute();
}
}
原学习视频地址
https://www.bilibili.com/video/BV17E411N7KN?p=1
原视频作者:
狂神说