Spring Data Jpa如何实现审计和乐观锁功能
[版权申明] 非商业目的注明出处可自由转载
出自:shusheng007
文章目录
- 概述
- 审计
- 引入依赖
- 启用日期的审计
- 启用用户相关审计
- 乐观锁
- 总结
- 源码
概述
在具体业务中跟踪数据库数据操作记录有时是很强烈的需求:谁何时创建了这条记录,谁何时修改了这条记录,越是大点的公司这个需求越强烈。这些需求实现较为机械和简单,所以我们不想手动去做,所以很多相关框架都提供了相应的方案,今天我们就看下Spring Data JPA是如何实现的。
审计
Spring Data JPA 其提供了4个相关的注解
@CreatedBy
@CreatedDate
@LastModifiedBy
@LastModifiedDate
上面四兄弟已经做到了顾名思义,所以不用解释他们具体是做什么的了吧。只要使用上面的四个注解标记了JAP的Entity类相应的字段,JPA就会自动给数据库填充数据,不用你自己手动去给这几个字段赋值了。
下面让我们实际操作一下
引入依赖
首先我们要使用spring-data-jpa,所以先要引入其依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
启用日期的审计
如果你不关心何人创建和修改,只关心数据的修改和更新时间的话,按照如下方法即可。
- 开启Jpa审计开关
@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
Jpa审计功能需要使用@EnableJpaAuditing
开启
- 使用相关注解标记
由于每个Jpa的实体(@Entity
标记的类,对应数据库中的表)都要实现审计功能,所以我们创建一个抽象基类给他们继承。
@Setter
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditable {
@CreatedDate
private Date createAt;
@LastModifiedDate
private Date updateAt;
}
其中@MappedSuperclass
申明这个类的字段会映射在其子类对应的表中,@EntityListeners(AuditingEntityListener.class)
设置拦截功能。Jpa在插入数据前通过AuditingEntityListener
插入数据。
- 使用
Jpa数据实体类直接继承审计基类即可
@Data
@Entity
@Table(name = "account")
public class Account extends AbstractAuditable{
...
}
至此,account表中的create_at 与update_at列就会被自动填充日期数据了。
启用用户相关审计
如果你还要记录是何人创建和修改的数据记录的话,就需要费点事,因为你的给Jpa提供用户信息。
所以我们要实现AuditorAware
接口,提供用户信息给程序。 这块如果使用SpringSecurity的话可以很方便的拿到用户信息。
public class AuditorAwareImpl implements AuditorAware<Long> {
@Override
public Optional<Long> getCurrentAuditor() {
//返回当前用户id
return Optional.of(1L);
}
}
将刚才实现的auditorAware的bean配置给Jpa
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaConfig {
@Bean
public AuditorAware<Long> auditorAware(){
return new AuditorAwareImpl();
}
}
在审计基类中加入用户信息。
@Setter
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditable<T> {
@CreatedBy
private T createdBy;
@CreatedDate
private Date createAt;
@LastModifiedBy
private T updatedBy;
@LastModifiedDate
private Date updateAt;
}
Jpa的Entity类继承审计基类
@Data
@Entity
@Table(name = "account")
public class Account extends AbstractAuditable<Long>{
...
}
至此,你已经完成了审计相关的功能。所以说现在对程序员的要求完全已经两级分化了,低的越来越低,高的越来越高。低的啥都不用明白,只要按部就能实现很复杂的功能,高的呢要求深刻理解内部的实现原理,以便掌控全局,不然出了问题都没有解决的思路。
乐观锁
几年前我第一次接触到数据库中的这个概念时还是很蒙圈的,真是万事开头难,现在看来那么稀松平常的事情但在最开始接触时感觉那么的晦涩难懂。
首先应该深刻理解程序中锁这个概念:锁是为了应对资源竞争而产生的,锁的方案汗牛充栋,有的异常复杂,像Redis的红锁。但我们这边要说的却很简单,就是数据库中有一条数据,可能被多个线程访问,为了防止数据竞争导致的问题,所以我们需要锁。在一个线程修改数据的时候,另一个就不能修改(等待或者放弃)。
- 悲观锁
王二狗:我抢到了令牌,现在开始修改数据了,谁TM都别动,等我修改完把令牌给你们…
众线程:我r,这没招只能等着
这个令牌就是把悲观锁。
- 乐观锁:
王二狗:这三更半夜的,应该没人修改数据,我修改一下试试,搞定提交,no,我r (version =1)
牛翠华:不好意思,二狗,我比你先修改的…( version=2)
王二狗:我r, 这写了半天天了,只能再重新写,搞定提交,yes,成功… (version =3)
众线程:在家睡觉…
数据库的乐观锁和上面描述的一样,数据表中存在一个version的列,每个线程每次成功提交的时候都会给这个version加1,提交的时候就会将自己持有的version(此线程从数据库读取的version加1)与数据库中的比较,只有持有的version值比数据库中的值大才能插入,否则放弃。
从上面的描述也应该看出乐观锁适用于竞争不激烈的场景,如果竞争非常激烈每次提交都不成功那乐观锁就失去了意义,那使用悲观锁较为妥当。
这个功能在SpringData Jpa中实现起来更简单,只需要一个@Version
注解
import javax.persistence.Version;
@Data
@Entity
@Table(name = "account")
public class Account extends AbstractAuditable<Long>{
@Version
private Long version;
}
如果你允许hibernate自动产生DDL sql语句的话数据库中就会多一个version列,如果不允许你就自己新加一列。然后每插入一条数据这个version就会加1。当两个线程对此条数据产生竞争时,有一个更新线程的sql是执行不成功的。
总结
以前一直在使用MyBatis,现在看来JPA也不赖,关键是平时用的是SpringData JPA,Spring全家桶给你安排的明明白白。这里也不是说MyBatis不好,还是的针对自己组织和项目来取舍。
又要过大年了,又要长一岁了…
源码
一如既往,你可以在首发文章下面找到源码:Spring Data Jpa如何实现审计和乐观锁功能