DML编程控制

id生成策略

DML编程控制_第1张图片
模型类:

@Data
@TableName("tbl_user")
public class User {
	@TableId(type = IdType.AUTO)
	@TableId(type = IdType.NONE)
	@TableId(type = IdType.INPUT)
	@TableId(type = IdType.ASSIGN_ID)
	@TableId(type = IdType.ASSIGN_UUID)
	private Long id;
	private String name;
	@TableField(value="pwd",select=false)
	private String password;
	private Integer age;
	private String tel;
	@TableField(exist=false)
	private Integer online;
}

配置文件:

# dataSource
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: root
# mp日志
mybatis-plus:
	configuration:
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

UserDao接口:

@Mapper
public interface UserDao extends BaseMapper<User> {
}

测试:

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
	@Autowired
	private UserDao userDao;
	@Test
	void testSave(){
		User user = new User();
		user.setName("黑马程序员");
		user.setPassword("itheima");
		user.setAge(12);
		user.setTel("4006184000");
		userDao.insert(user);
	}
}

DML编程控制_第2张图片
分布式策略ID:
当数据量足够大的时候,一台数据库服务器存储不下,这个时候就需要多台数据库服务器进行存储。比如订单表就有可能被存储在不同的服务器上。如果用数据库表的自增主键,因为在两台服务器上所以会出现冲突。这个时候就需要一个全局唯一ID,这个ID就是分布式ID。

1.AUTO策略
AUTO的作用是使用数据库ID自增,在使用该策略的时候一定要确保对应的数据库表设置了ID主键自增,否则无效。

2.NONE策略
不设置id生成策略

3.INPUT策略
用户手工输入id,需要将表的自增策略删除掉
如果没有设置主键ID的值,则会报错,错误提示就是主键ID没有给值

4.ASSIGN_ID策略
雪花算法生成id(可兼容数值型与字符串型)。这种生成策略,不需要手动设置ID,如果手动设置ID,则会使用自己设置的值。
生成的ID就是一个Long类型的数据。

5.ASSIGN_UUID策略
以UUID生成算法作为id生成策略。主键的类型不能是Long,而应该改成String类型。
主键类型设置为varchar,长度要大于32,因为UUID生成的主键为32位,如果长度小的话就会导致插入失败。

6.策略对比

  • NONE:不设置id生成策略,MP不自动生成,约等于INPUT,所以这两种方式都需要用户手动设置,但是手动设置第一个问题是容易出现相同的ID造成主键冲突,为了保证主键不冲突就需要做很多判定,实现起来比较复杂
  • AUTO:数据库ID自增,这种策略适合在数据库服务器只有1台的情况下使用,不可作为分布式ID使用
  • ASSIGN_UUID:可以在分布式的情况下使用,而且能够保证唯一,但是生成的主键是32位的字符串,长度过长占用空间而且还不能排序,查询性能也慢
  • ASSIGN_ID:可以在分布式的情况下使用,生成的是Long类型的数字,可以排序性能也高,但是生成的策略和服务器时间有关,如果修改了系统时间就有可能导致出现重复主键

全局优化配置

1.ID策略全局优化
如何让所有的模型类都可以使用同一个主键ID策略呢?
要在配置文件中添加如下内容:

mybatis-plus:
	global-config:
		db-config:
			id-type: assign_id

配置完成后,每个模型类的主键ID策略都将成为assign_id

2.数据库表与模型类的映射关系全局优化
MP会默认将模型类的类名名首字母小写作为表名使用,假如数据库表的名称都以tbl_开头,那么我们就需要将所有的模型类上添加@TableName,如何让所有的模型类都可以使用同一个数据库表映射策略呢?

mybatis-plus:
	global-config:
		db-config:
			table-prefix: tbl_

设置表的前缀内容,这样MP就会拿 tbl_加上模型类的首字母小写,就刚好组装成数据库的表名

多记录操作

1.多条删除的实现

//删除(根据ID 批量删除),参数是一个集合,可以存放多个id
//int deleteBatchIds(@Param(Constants.COLLECTION) Collection idList);

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
	@Autowired
	private UserDao userDao;
	@Test
	void testDelete(){
		//删除指定多条数据
		List<Long> list = new ArrayList<>();
		list.add(1402551342481838081L);
		list.add(1402553134049501186L);
		list.add(1402553619611430913L);
		userDao.deleteBatchIds(list);
	}
}

2.批量查询的实现

//查询(根据ID 批量查询),参数是一个集合,可以存放多个id值。
//List selectBatchIds(@Param(Constants.COLLECTION) Collection idList);

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
	@Autowired
	private UserDao userDao;
	@Test
	void testGetByIds(){
	//查询指定多条数据
	List<Long> list = new ArrayList<>();
		list.add(1L);
		list.add(3L);
		list.add(4L);
		userDao.selectBatchIds(list);
	}
}

逻辑删除

DML编程控制_第3张图片

  • 这是一个员工和其所签的合同表,关系是一个员工可以签多个合同,是一个一(员工)对多(合同)的表
  • 员工ID为1的张业绩,总共签了三个合同,如果此时他离职了,我们需要将员工表中的数据进行删除,会执行delete操作
  • 如果表在设计的时候有主外键关系,那么同时也得将合同表中的前三条数据也删除掉

DML编程控制_第4张图片

  • 后期要统计所签合同的总金额,就会发现对不上,原因是已经将员工1签的合同信息删除掉了
  • 如果只删除员工不删除合同表数据,那么合同的员工编号对应的员工信息不存在,那么就会出现垃圾数据,就会出现无主合同,根本不知道有张业绩这个人的存在
  • 所以经过分析,我们不应该将表中的数据删除掉,而是需要进行保留,但是又得把离职的人和在职的人进行区分,这样就解决了上述问题

DML编程控制_第5张图片

  • 区分的方式,就是在员工表中添加一列数据deleted,如果为0说明在职员工,如果离职则将其改完1,(0和1所代表的含义是可以自定义的)

所以对于删除操作业务问题来说有:
物理删除:业务数据从数据库中丢弃,执行的是delete操作
逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中,执行的是update操作

DML编程控制_第6张图片
数据库表添加列:

DML编程控制_第7张图片

新的实体类:

@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
	@TableId(type = IdType.ASSIGN_UUID)
	private String id;
	private String name;
	@TableField(value="pwd",select=false)
	private String password;
	private Integer age;
	private String tel;
	@TableField(exist=false)
	private Integer online;
	@TableLogic(value="0",delval="1")
	//value为正常数据的值,delval为删除数据的值
	private Integer deleted;
}

新的删除方法:

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
	@Autowired
	private UserDao userDao;
	@Test
	void testDelete(){
		userDao.deleteById(1L);
	}
}

DML编程控制_第8张图片
从测试结果来看,逻辑删除最后走的是update操作,会将指定的字段修改成删除状态对应的值。

执行查询操作:

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
	@Autowired
	private UserDao userDao;
	@Test
	void testFind(){
		System.out.println(userDao.selectList(null));
	}
}

DML编程控制_第9张图片
可想而知,MP的逻辑删除会将所有的查询都添加一个未被删除的条件,也就是已经被删除的数据是不应该被查询出来的。

全部查询

@Mapper
public interface UserDao extends BaseMapper<User> {
	//查询所有数据包含已经被删除的数据
	@Select("select * from tbl_user")
	public List<User> selectAll();
}

全局配置优化

全局配置:

mybatis-plus:
	global-config:
		db-config:
			# 逻辑删除字段名
			logic-delete-field: deleted
			# 逻辑删除字面值:未删除为0
			logic-not-delete-value: 0
			# 逻辑删除字面值:删除为1
			logic-delete-value: 1

乐观锁

业务并发现象带来的问题:

  • 假如有100个商品或者票在出售,为了能保证每个商品或者票只能被一个人购买,如何保证不会出现超买或者重复卖
  • 对于这一类问题,其实有很多的解决方案可以使用
  • 第一个最先想到的就是锁,锁在一台服务器中是可以解决的,但是如果在多台服务器下锁就没有办法控制,比如12306有两台服务器在进行卖票,在两台服务器上都添加锁的话,那也有可能会导致在同一时刻有两个线程在进行卖票,还是会出现并发问题
  • 我们接下来介绍的这种方式是针对于小型企业的解决方案,因为数据库本身的性能就是个瓶颈,如果对其并发量超过2000以上的就需要考虑其他的解决方案了。

实现思路

  • 数据库表中添加version列,比如默认值给1
  • 第一个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
  • 第二个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
  • 第一个线程执行更新时,set version = newVersion where version = oldVersion
    • newVersion = version+1 [2]
    • oldVersion = version [1]
  • 第二个线程执行更新时,set version = newVersion where version = oldVersion
    • newVersion = version+1 [2]
    • oldVersion = version [1]
  • 假如这两个线程都来更新数据,第一个和第二个线程都可能先执行
  • 假如第一个线程先执行更新,会把version改为2,
  • 第二个线程再更新的时候,set version = 2 where version = 1,此时数据库表的数据version已经为2,所以第二个线程会修改失败
  • 假如第二个线程先执行更新,会把version改为2,
  • 第一个线程再更新的时候,set version = 2 where version = oldVersion
  • 此时数据库表的数据version已经为2,所以第一个线程会修改失败
  • 不管谁先执行都会确保只能有一个线程更新数据,这就是MP提供的乐观锁的实现原理分析。

数据库表添加列:
DML编程控制_第10张图片
新的模型类:

@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
	@TableId(type = IdType.ASSIGN_UUID)
	private String id;
	private String name;
	@TableField(value="pwd",select=false)
	private String password;
	private Integer age;
	private String tel;
	@TableField(exist=false)
	private Integer online;
	private Integer deleted;
	@Version
	private Integer version;
}

乐观锁拦截器:

@Configuration
public class MpConfig {
	@Bean
	public MybatisPlusInterceptor mpInterceptor() {
		//1.定义Mp拦截器
		MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
		//2.添加乐观锁拦截器
		mpInterceptor.addInnerInterceptor(new
		OptimisticLockerInnerInterceptor());
		return mpInterceptor;
	}
}

执行更新操作:

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
	private UserDao userDao;
	@Test
	void testUpdate(){
		//1.先通过要修改的数据id将当前数据查询出来
		User user = userDao.selectById(3L); //version=3
		User user2 = userDao.selectById(3L); //version=3
		//2.将要修改的属性逐一设置进去
		user2.setName("Jock aaa");
		userDao.updateById(user2); //version=>4
		user.setName("Jock bbb");
		userDao.updateById(user); //verion=3?条件还成立吗?
	}
}


官方文档:
https://baomidou.com/pages/0d93c0/#optimisticlockerinnerinterceptor

你可能感兴趣的:(spring学习笔记,mybatis,java,数据库)