字段映射与表名映射
问题一:当数据库表字段与编码属性设计不同步 如password与pwd
可以用mp的TableField里面的value属性即可关联
问题二:编码添加了数据库中不存在的字段属性 如“是否在线”一类的属性明显不需要放入数据库,但是在查询时候java需要在数据库中查,此时sql会报错
同样用TableField里面的exist=false即可
问题三:采用默认查询开放了更多的字段查看权限 如想要查整张表,但是命令会把密码也查了,操作不安全
TableField使用select属性设定它为false 则在查询过程中就不会被查询
问题四:表名与编码开发设计不同步
与问题一相似解,在类上面加一个TableName(“数据库表名”)
TableId见下面
如果觉得每次都要在属性上面一行加@TableXX不方便 可以在yml里面统一设置
1、yml文件
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
username: root
password: 123456
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
username: root
password: 123456
mapper-locations:
classpath:mybatis/mapper/*.xml
main:
banner-mode: off
mybatis-plus:
global-config:
banner: false
db-config:
id-type: assign_id
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
configuration:
#开启mp的日志(输出到控制台)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# table-prefix:
//增
@Test
void testSave(){
User user = new User();
user.setName("熊");
user.setPassword("123456");
user.setAge(10);
user.setTel("400012");
userDao.insert(user);
}
//删
@Test
void testDelete(){
userDao.deleteById(1618522903760699393L);
}
//改
void testUpdate(){
User user = new User();
user.setId(1L);
user.setName("大笨蛋");
userDao.updateById(user);
}
//查
//查单个
@Test
void testGetById(){
User user = new User();
user = userDao.selectById(2L);
System.out.println(user);
}
//查全部
@Test
void testGetAll() {
List<User> userList = userDao.selectList(null);
System.out.println(userList);
}
引入lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
@Data @NoArgsConstructor @AllArgsConstructorc
查询
按条件查
void testGetAll() {
//方式一:按条件查询对应的操作
QueryWrapper wrapper = new QueryWrapper();
//可以对wrapper进行设置来添加条件
wrapper.lt("age",18);
List<User> userList = userDao.selectList(wrapper);
System.out.println(userList);
}
为防止字段的手动输入可能出错检查不出,则有lamda表达式第二种查询方式
//方式二:按条件查询对应的操作
QueryWrapper<User> wrapper = new QueryWrapper<User>();
//可以对wrapper进行设置来添加条件
wrapper.lambda().lt(User::getAge,10);
List<User> userList = userDao.selectList(wrapper);
System.out.println(userList);
方法二另一种写法
//方式三:按条件查询对应的操作
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//可以对wrapper进行设置来添加条件
lqw.lt(User::getAge,10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
前面是and查询 下面是或查询
//小于10岁或者大于30岁
lqw.lt(User::getAge,10).or().gt(User::getAge,30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
封装查询条件
1、null值处理
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//先判断第一个条件是否为true 如果为true 则连接当前条件
lqw.lt(null!=uq.getAge(),User::getAge,uq.getAge2())
.gt(null!=uq.getAge2(),User::getAge,uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
2、设置查询字段,没被选中的查出来为空
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId,User::getName,User::getAge);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
下面写法相同结果
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id","name","age","tel");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
3、统计有多少数据
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*)");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as 行数");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);
4、分组(查询投影)
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as 行数","tel");
lqw.groupBy("tel");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);
假如有条件或者函数不支持查 则回到mybatis在userdao类里@select 以前写的设类型去
5、等匹配
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
如果像上面的只用查一条数据则可以用专门的selectOne
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");
User user = userDao.selectOne(lqw);
System.out.println(user);
实际登陆业务中要做Md5加密
//范围查询用到的:lt,le,gt,ge,eq,between( 这么记带等号的le=lt+equal)
//between 前面放左区间
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.between(User::getAge,10,30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
//like 模糊查询 like=% likeleft=%_ likeright=_%、
非全文检索版,要是查不能单独这样
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.like(User::getName,"J");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
还有其它的符号 条件构造器 | MyBatis-Plus (baomidou.com)
可以先点“.”先写一下,不行再去查
分页查询
select * from ?????? limit 1,2 (忽略前1条数据,获取后2条数据)
注意limit后面参数和pageNum,pageSize不是一个
所以不用转换:
在原有基础上追加功能
配置拦截器,开启分页拦截器
@Configuration
public class Mpconfig {
@Bean
public MybatisPlusInterceptor mpInterceptor(){
//定义MP的拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
//在拦截器中添加具体的拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
//分页查询
@Test
void testGetByPage(){
IPage page = new Page(1,2);
userDao.selectPage(page,null);
System.out.println("当前页码值"+page.getCurrent());
System.out.println("每页显示数"+page.getSize());
System.out.println("一共多少页"+page.getPages());
System.out.println("一共多少条数据"+page.getTotal());
System.out.println("数据:"+page.getRecords());
}
DML编程控制
增加的主键生成问题 如发票,外卖单号
TableId
设置id生成策略
原来是生成id是乱的默认是雪花算法生成唯一的ID 下面代码就可以设置成数据库我们设置的自增策略
@TableId(type = IdType.AUTO)
None是没有指定,Input是自己指定Id否则报错(已设置ID不能为空)ASSIGN_ID 64位2进制
所以ID最好用bigInt来存
如果觉得每次都要在属性上面一行加@TableXX不方便 可以在yml里面统一设置
其它的比如表的前缀统一设置等等:
多数据查询-删除与查询
多数据删除
1、利用集合实现
@Test
void testDelete(){
List<Long>list=new ArrayList<>();
list.add(1618963665719726084L);
list.add(1618963665719726083L);
list.add(1618963665719726082L);
userDao.deleteBatchIds(list);
}
多数据查询
List<Long>list=new ArrayList<>();
list.add(1L);
list.add(2L);
list.add(4L);
userDao.selectBatchIds(list);
mp支持多数据删除和查询,只需要提供id
逻辑删除
为了避免多表删除造成业务无法查询统计,部分记录无法读取或者脏数据,最好在表中加一个字段来标明是否删除来代替真正从表中删除
此时再删除就不会删除表中的数据,且deleted字段的值由默认的0变成1
userDao.deleteById(1L)
此时mp接口删除实际上是执行update的sql语句操作, 并且在查询时候mp会在每次查询的条件中增加where deleted = 0 这个条件
在全局配置里设置逻辑操作的设定,使得不需要在实体类里加,只需要提供字段即可
如果想要查询被删字段,则需要通过编写sql语句查询
乐观锁
避免并发操作,2000个访问以下可以来尝试
加一个字段来保存
这个@Version会使得每次更改都会让version+1
开个拦截器(乐观锁拦截器)
执行update操作,如果没有version则不会执行mp额外的sql语句操作
加上version后
验证version是否为1,验证正确
此时自动将version+1存入
以下两种方法同理,后者是自动获取了version
@Test
void testUpdate(){
// User user = new User();
// user.setId(3L);
// user.setName("Jock666");
// user.setVersion(1);
// userDao.updateById(user);
//1、先通过要修改的数据id将当前数据查询出来
User user = userDao.selectById(3L);
//2.将要修改的属性逐一设置进去
user.setName("Jock888");
userDao.updateById(user);
}
模拟A、B用户进行并发访问
//模拟A、B用户秒杀
User user = userDao.selectById(3L); //version=3
User user2 = userDao.selectById(3L); //version=3
//先让B用户修改完成
user2.setName("Jockbbb");
userDao.updateById(user2); //version->4
//再让A用户进行修改
user.setName("Jockaaa");
userDao.updateById(user); //version=3?条件会成立吗 不成立
结果为先修改的B用户设置的结果
总结乐观锁的步骤:
1、加个字段设置默认值
2、在实体类中设置属性
3、到MP拦截器中加一个拦截器