常用注解
@TableName
操作数据库表时,Mapper接口继承BaseMapper,泛型名和数据库表名对应,如果数据表名为t_user,而BaseMapper的泛型为实体类User,导致找不到数据库的表。
1)解决方法1:实体类使用@TableName注解,value值为表名
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {
private Long id;
private String name;
private String sex;
private Integer age;
}
2)解决方法2,如果多张表的表名为t_user/t_cat/t_xxx,不需要为每一个实体类添加@TableName注解,在MyBatis全局配置即可,为所有表名添加前缀
mybatis-plus:
global-config: # MyBatisPlus全局配置
db-config: # 配置数据库
table-prefix: t_ #配置表名前缀为t_
@TableId
MyBatisPlus在实现CRUD默认会将Id作为主键,在插入数据时,如果主键不叫Id则添加功能会失败
解决方案:@TableId注解标识属性,将此属性对应的字段指定为主键
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {
// 将当前属性所对应的字段作为主键
@TableId
private Long id;
private String name;
private String sex;
private Integer age;
}
1)value属性
问题:实体类中被标识为主键的属性名为id,而数据库的主键为uid,则id属性不会对应uid字段上
解决方案:使用@TableId的value属性设置当前主键字段的字段名为uid
package com.dfbz.entity;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(value = "uid")
private Long id;
private String name;
private String sex;
private Integer age;
}
2)主键策略
在枚举类IdType中定制有如下几种注解策略:
public enum IdType {
// 数据自增(该类型请确保数据库设置了 ID自增 否则无效)
AUTO(0),
// 采用雪花算法生成ID
NONE(1),
// 交由用户自己分配ID(必须分配,不分配则出现异常,除非数据库表本身设置了自增)
INPUT(2),
// 采用雪花算法((主键类型为number或string)
// 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
ASSIGN_ID(3),
// 分配UUID (主键类型为 string)
// 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
ASSIGN_UUID(4),
// 不推荐使用,推荐使用ASSIGN_ID
@Deprecated
ID_WORKER(3),
// 不推荐使用,推荐使用ASSIGN_ID
@Deprecated
ID_WORKER_STR(3),
// 不推荐使用,推荐使用ASSIGN_UUID
@Deprecated
UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
// 使用数据库自增策略
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String sex;
private Integer age;
}
全局配置自增策略:
mybatis-plus:
global-config: # MyBatisPlus全局配置
db-config: # 配置数据库
id-type: auto # 统一设置id生成策略
@TableField
如果实体类的普通属性名,和数据库非主键的字段名不一致解决方案:@TableField设置对应字段名
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("user_name")
private String name;
@TableField("user_sex")
private String sex;
@TableField("user_age")
private Integer age;
}
@TableLogic
在实际开发中,我们删除一条记录可能只是修改其状态,并不是真正的从数据库中删除掉;这样的删除成为逻辑删除;
逻辑删除:表中设置字段为删除状态 比如删除为1 未删除为0 则查询时,只会查到状态为0的数据(可以进行数据恢复)。
物理删除:从表中删除。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
/*
表示该列为逻辑删除字段
0: 表示未删除
1: 表示已删除
*/
@TableLogic
private Integer isDelete;
}
@EnumValue
在数据库中,经常会有一些字段符合枚举类型;
例如:
sex:0代表男,1代表女
status:0代表未开始,1代表进行中,2代表已结束
is_secret:0代表私密,1代表公开等
is_mark:0代表未签收,1代表拒签,2代表已签收
我们通常都是将以上字段设置为char或者int类型,然后通过对应的字符/数字代表对应的含义,然后到Java代码中我们通常都需要做类似于如下的判断
<span>${sex==0?'男':'女'}</span>
<if test=${status==0}>未开始</if>
<if test=${status==1}>进行中</if>
<if test=${status==2}>已结束</if>
if(status=0){
// 未开始....
}else if(status==1){
// 进行中
} else if(status==2){
已结束
}
以上代码比较繁琐,并且可读性不是很高;
在MyBatisPlus中支持我们定义一个枚举与数据库中的字段对应起来,然后在枚举类中,使用@EnumValue注解标注真实的值(与数据库的字段对应),然后定义一个String类型的字段表示这个枚举项代表的字符串含义;
public enum SexEnum {
MAN(0, "男"),
WOMAN(1, "女");
// 这一列的值和数据库表映射
@EnumValue
private Integer sexValue;
// 这个字段就是这个枚举项的字符串含义
private String sexName;
private SexEnum(Integer sexValue, String sexName) {
this.sexValue = sexValue;
this.sexName = sexName;
}
}
扫描枚举包:
#配置日志
mybatis-plus:
# 扫描枚举包
type-enums-package: com.dfbz.enum_
定义实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
/*
使用枚举类型
当从数据库中查询到了0则赋值SexEnum.MAN给sex变量
当从数据库中查询到了1则赋值SexEnum.WOMAN给sex变量
*/
private SexEnum sex;
}
@Version
1)乐观锁与悲观锁
在多个客户端(线程/进程)操作同一个资源并发生写的操作时,我们就需要保证数据的安全性了。
如图所示:
当我们在购买车票时,首先会进行票数的查询,例如A1在购买车票时查询到票还有100张,准备购买第100张票,与此同时,A2也查询到票数为100,也将购买第100张票;
在并发修改某一资源时,我们必须保证线程安全的问题。在操作之前先加锁,这种方式就是采用悲观锁的方式;
悲观锁的概念:悲观锁简单的理解就是程序处于悲观状态,在操作任何资源时都认为其他程序会来修改当前资源,自己不放心,因此在操作资源时加锁。
悲观锁虽然保证了程序的安全性,同时效率也降低了很多,在一个客户端操作时,其他客户端均不可操作,降低了系统的并发性能。
乐观锁概念:乐观锁简单理解就是程序一直处于乐观状态,在操作任何资源时认为其他程序不会来修改当前资源,整个过程不加锁,不加锁效率是可以保证,但不是又回到了我们最初的那个状态吗?即线程安全问题。
我们可以在每条记录中分配一个_version字段,每当我们对记录进行更新时,此版本号都会自增。我们可以借助该字段帮我们完成乐观锁。保证线程安全问题。
-- 要修改数据之前,先查该数据上一次修改的时间戳
select version from table_ where id=1;
-- 修改数据时,更新版本号
update table_ set goods_name='小苹果', version=version+1 where version=${version};
@Version实现乐观锁
package com.dfbz.entity;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer count;
@Version // 乐观锁字段
private Integer version;
}
Mapper接口:
import org.springframework.stereotype.Repository;
@Repository
public interface GoodsMapper extends BaseMapper<Goods> {
}
测试代码:
@Autowired
private GoodsMapper goodsMapper;
@Test
public void test6() throws Exception {
Goods goods = goodsMapper.selectById(1L);
goods.setCount(goods.getCount() - 1);
goodsMapper.updateById(goods); // 修改完毕后,version在本次version的基础上+1
}
MyBatis Plus绑定Mapper.xml
1)在application.yml中扫描Mapper.xml:
#配置日志
mybatis-plus:
# 扫描mapper.xml文件
mapper-locations: classpath:com/dfbz/mapper/*.xml
# 配置包别名
type-aliases-package: com.dfbz.entity
2)在Mapper接口中扩展方法:
@Repository
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
3)编写Mapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace 名称空间,指定对哪个接口进行映射-->
<mapper namespace="com.dfbz.mapper.UserMapper">
<select id="findAll" resultType="user">
select * from user
</select>
</mapper>
Mapper的分页查询
Mapper分页查询配置
在MyBatis中提供有Page对象来帮助我们实现分页查询,在Page对象中有如下成员:
MyBatisPlus的分页逻辑底层是通过分页插件来完成的,因此我们首先要配置MyBatisPlus的分页插件;
@Configuration
@MapperScan("com.dfbz.mapper") // mapper接口的所在位置
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
Mapper完成分页查询
在BaseMapper中主要提供有如下方法来完成分页查询:
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper):
参数1:分页配置类
参数2:分页查询条件
解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为指定类型
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper)
参数1:分页配置类
参数2:分页查询条件
解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为Map类型
1)无条件分页查询
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
public class Demo06_BaseMapper的分页查询 {
@Autowired
private UserMapper userMapper;
/**
* 无条件分页查询
* @throws Exception
*/
@Test
public void test1() throws Exception {
// 封装分页信息
Page<User> page = new Page<>(1,3);
/*
执行分页查询,并将结果封装到page中
参数1: 分页配置
参数2: 查询条件
*/
userMapper.selectPage(page, null);
// 当前页数据
List<User> pageData = page.getRecords();
for (User user : pageData) {
System.out.println(user);
}
System.out.println("------------");
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}
}
2)带条件分页查询:
/**
* 带条件分页查询
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
QueryWrapper<User> wrapper = Wrappers.query();
wrapper.like("name", "a");
// 封装分页信息
Page<User> page = new Page<>(1, 3);
/*
执行分页查询,并将结果封装到page中
参数1: 分页配置
参数2: 查询条件
*/
userMapper.selectPage(page, wrapper);
// 当前页数据
List<User> pageData = page.getRecords();
for (User user : pageData) {
System.out.println(user);
}
System.out.println("------------");
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}
3)将分页数据的查询结果以Map类型返回
/**
* 查询结果以Map返回
*
* @throws Exception
*/
@Test
public void test3() throws Exception {
// 封装分页信息
Page page = new Page<>(1, 3);
userMapper.selectMapsPage(page, null);
// 每一条记录都是一个HashMap
List<HashMap<String,Object>> pageData = page.getRecords();
for (HashMap userMap : pageData) {
System.out.println(userMap);
}
System.out.println("------------");
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}
原文链接:https://blog.csdn.net/Bb15070047748/article/details/129212543