条件查询:
MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。
方式一:按条件查询
QueryWrapper qw = new QueryWrapper();
qw.lt("age",18); //age小于18
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
方式二:lambda格式按条件查询
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge, 10);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
方式三:lambda格式按条件查询
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
并且与或者关系:
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
并且关系:10到30岁之间
//lqw.lt(User::getAge, 30).gt(User::getAge, 10);
或者关系:小于10岁或者大于30岁
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
常规格式:
qw.lt("age",65);
qw.lt("age",18);
链式编程格式:
qw.lt("age",65).qw.lt("age",18);
lambda格式1:
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge, 10);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
lambda格式2:
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
Null值处理:
//模拟页面传递过来的查询数据
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//先判定第一个参数是否为true,如果为true连接当前条件
lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2());
lqw.gt(null != uq.getAge(),User::getAge, uq.getAge());
//链式编程
lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2())
.gt(null != uq.getAge(),User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
查询投影:
//lambda格式:
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId,User::getName,User::getAge);
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id","name","age","tel");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
----------------------------------------------------------------------------
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count, tel");
lqw.groupBy("tel");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);
----------------------------------------------------------------------------
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//等同于=
lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt(<) le(<=) gt(>) ge(>=) eq(=) between
lqw.between(User::getAge,10,30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//模糊匹配 like
/*
likeLeft:%xx
likeRight:xx%
*/
lqw.likeLeft(User::getName,"J");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
字段映射与表命映射:
问题一、表字段与编码属性设计不同步
名称:@TableField
类型:属性注解
位置:模型类属性定义上方
作用:设置当前属性对应的数据库表中的字段关系
范例:
public class User{
@TableField(value="pwd")
private String password;
}
相关属性:
value(默认):设置数据库表字段名称
问题二:编码中添加了数据库未定义的属性
@TableField(exist = false)
private Integer online;
相关属性:
exist:设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用
问题三:采用默认查询开放了更多的字段查看权限
@TableField(value="pwd",select = false)
private String password;
问题四:表名与编码开发设计不同步
@TableName("tbl_user")
public class User{
private Long id;
}
主键生成策略控制:
雪花算法:
- Snowflake Algorithm)是 Twitter 公司开发的一个全局唯一 ID 生成算法。它可以在分布式系统环境下,生成唯一、有序、时间戳型的 ID 号码。雪花算法的核心思想是将一个 64 位的二进制整数,划分成多个部分,每个部分用来表示不同的信息。具体来说,雪花算法将 64 位的整数划分为以下几个部分:
1、首位固定为 0,表示这是一个正整数。
2、接着的 41 位表示毫秒级时间戳,可以用来记录生成该 ID 的时间。
3、然后是 10 位的机器标识号,可以用来区分不同的机器或者节点。
4、最后是 12 位序号,可以用来表示在同一毫秒内生成的不同 ID。
占位符0|时间戳(41)|机器码(5+5)|序列号(12)
- 通过以上划分,雪花算法可以在分布式环境下确保生成的 ID 具有全局唯一性,并且由于采用时间戳和序号组合的方式,可以保证 ID 生成时的有序性,同时也避免了因使用分布式算法而引入的性能瓶颈。
- 需要注意的是,雪花算法并不能完全解决全局唯一 ID 生成问题,例如,如果机器号不唯一或者机器时间不同步等情况,都会导致生成的 ID 不唯一。此外,由于雪花算法在高并发环境下产生 ID 的时候需要锁住序号位,因此可能会存在性能瓶颈问题,在实际使用中需要根据具体情况进行权衡。
不同的表应用不同的id生成策略:
日志:自增(1,2,3,4,……)
购物订单:特殊规则(FQ23948AK3843)
外卖单:关联地区日期等信息(10 04 20200314 34 91)
关系表:可省略id
……
@TableId:
类型:属性注解
位置:模型类中用于表示主键的属性定义上方
作用:设置当前类中主键属性的生成策略
范例:
public class User{
@TableId(type = IDType.AUTO)
private Long id;
}
相关属性:
vlaue:设置数据库主键名称
type:设置主键属性的生成策略,值参照IdType枚举值
id生成策略控制:
- AUTO(0):使用数据库id自增策略控制id生成
- NONE(1):不设置id生成策略
- INPUT(2):用户手工输入id
- ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型)
- ASSIGN_UUID(4):以UUID生成算法作为id生成策略
application.yml
mybatis-plus:
global-config:
db-config:
id-type: assign_id
等同于:
@TableId(type = IdType.ASSING_ID)mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
相当于:
@TableName(“tbl_score”)
publie class Score{
@TableId(type = IdType.AUTO)
private Long id;
}
多条数据操作(删除与查询):
void testDelete(){
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(2L);
list.add(3L);
//批量删除
userDao.deleteBatchIds(list);
//批量查询
List<Long> list2 = new ArrayList<>();
list.add(11L);
list.add(12L);
list.add(13L);
//批量查询
userDao.selectBatchIds(list);
}
按照主键删除多条记录:
List<Long> ids = Arrays.asList(new Long[]{2,3});
userDao.deleteBatchIds(ids);
根据主键查询多条记录:
List<Long> ids = Arrays.asList(new Long[]{2,3});
List<User> userList = userDao.selectBatchIds(ids);
逻辑删除:
- 数据库表中添加逻辑删除标记字段(别忘记给默认值)
-
实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
public class User{
private Long id;
@TableLogic
private Integer deleted;
} -
配置逻辑删除字段值
mybatis-plus:
globle-config:
db-config:
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑删除字面值:未删除为0
logic-not-delete-value: 0
# 逻辑删除字面值:删除为1
logic-delete-value: 1
执行SQL语句:UPDATE tbl_user SET deleted=1 WHERE id=? AND deleted=0
删除操作业务问题:业务数据从数据库中丢弃
逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中
-------------------------------------------------
@TableLogic(value = "0",delval = "1")
//value默认逻辑未删除值(该值可无、会自动获取全局配置),delval默认逻辑删除之(该值可无、会自动获取全局配置)
private Integer deleted;
配置逻辑删除之后,执行的SQL语句就会
由原来的:delete ……
变为update ……
-------------------------------------------------
application.yml
mybatis-plus:
……
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑删除字面值:未删除为0
logic-not-delete-value: 0
# 逻辑删除字面值:删除为1
logic-delete-value: 1
-------------------------------------------------
乐观锁:
业务并发现象带来的问题:秒杀
-------------------------------------------------
MpConfig.java
//添加乐观锁拦截器
mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
-------------------------------------------------
void testUpdate(){
//1.先通过要修改的数据id将当前数据查询出来
User user = userDao.selectById(3L); //version=3
User user2 = userDao.selectById(3L); //version=3
user2.setName("Jock aaa");
userDao.updateById(user2); //version=>4
user.setName("Jock bbb");
userDao.updateById(user); //verion=3条件不成立了,修改失败
}
执行修改前先执行查询语句:
SELECT id,name,age,tel,deleted,version FROM tbl_user WHERE id=?
执行修改时使用vwesion字段作为乐观锁检查依据:
UPDATE tbl_user SET name=?,age=?,tel=?,vwesion=? WHERE id=? AND version=?
代码生成器:
模板:MyBatisPlus提供
数据库相关配置:读取数据库获取信息
开发者自定义配置:手工配置
------------------------------------------------
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
------------------------------------------------
Generator.java
public class Generator {
public static void main(String[] args) {
AutoGenerator autoGenerator = new AutoGenerator();
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("root");
autoGenerator.setDataSource(dataSource);
autoGenerator.execute();
}
}
------------------------------------------------
CodeGenerator.java
//设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java"); //设置代码生成位置
globalConfig.setOpen(false); //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("我是作者"); //设置作者
globalConfig.setFileOverride(true); //默认为false,设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao"); //设置数据层接口名,%s为占位符,指代模块名称,如果不配置这项,就生成默认的名称:XXXMapper
globalConfig.setIdType(IdType.ASSIGN_ID); //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
------------------------------------------------
//设置包名相关配置
PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.aaa"); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("domain"); //设置实体类包名
packageInfo.setMapper("dao"); //设置数据层包名
autoGenerator.setPackageInfo(packageInfo);
------------------------------------------------
//策略设置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tbl_user"); //设置当前参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("tbl_"); //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 例如: User = tbl_user - tbl_
strategyConfig.setRestControllerStyle(true); //设置是否启用Rest风格
strategyConfig.setVersionFieldName("version"); //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted"); //设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true); //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);
------------------------------------------------