spring-boot - Spring JPA 事务 ID
问题描述
我为我的所有实体添加了一个属性 - 事务 id - 这是一个序列生成的值,我在每个事务中增加一次。
我还将事务 ID 与用户和开始/结束时间一起存储,因此我对数据库中的每个更改都有一个审计跟踪。
处理存储完整图形的最佳方法是什么,我基本上只想将事务 id 应用于那些实际脏的实体?
我可以在事务 id 列上放置 @PrePersist 和 @PreUpdate,但是如何检索当前事务 id 的值?有没有办法在事务对象或其他 JPA 控制器上存储和检索值?我需要使用 ThreadLocal 解决方案吗?
解决方案
好的,这就是我所做的。它似乎适用于所有用例,尽管我没有进行任何性能测试等。如果有人看到任何可能不是最佳的或在某些情况下可能失败的东西,请指出。
这是所有 @Service 实现必须扩展的基础服务类:
public class BaseService
{
private final ActivityService activityService;
private final ApplicationEventPublisher applicationEventPublisher;
public static ThreadLocal<Activity> transaction = new ThreadLocal<>();
public BaseService(ActivityService activityService, ApplicationEventPublisher applicationEventPublisher)
{
this.activityService = activityService;
this.applicationEventPublisher = applicationEventPublisher;
}
Object executeWithinActivity(Updater updater)
{
boolean startedLocally = false;
try
{
if (transaction.get() == null)
{
startedLocally = true;
Activity activity = activityService.startTransaction();
transaction.set(activity);
}
return updater.execute(transaction.get());
}
finally
{
if (startedLocally)
{
applicationEventPublisher.publishEvent(new TransactionEvent());
Activity activity = transaction.get();
activityService.endTransaction(activity);
}
}
}
protected interface Updater
{
Object execute (Activity activity);
}
static class TransactionEvent
{
}
}
Activity 是表示存储的事务 id 的实体:
@Entity
@Getter @Setter
@Table(name = "transactions", schema = "public", catalog = "euamdb")
public class Activity
{
@Id
@Column(name = "transaction_id", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tx_generator")
@SequenceGenerator(name = "tx_generator", sequenceName = "transaction_seq", allocationSize = 1)
private long transactionId;
@Basic
@Column(name = "user_id", length = 24)
private String userId;
@Basic
@Column(name = "transaction_start")
@CreationTimestamp
private Date transactionStart;
@Basic
@Column(name = "transaction_end")
@UpdateTimestamp
private Date transactionEnd;
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (!(o instanceof Activity)) return false;
Activity that = (Activity) o;
return transactionId == that.transactionId;
}
@Override
public int hashCode()
{
return Long.hashCode(transactionId);
}
}
ActivityService(不扩展 BaseService):
@Service
public class ActivityService
{
private final ActivityRepository activityRepository;
private final AuthUserService authService;
@Autowired
public ActivityService(ActivityRepository activityRepository, AuthUserService authService)
{
this.activityRepository = activityRepository;
this.authService = authService;
}
@Transactional
public Activity startTransaction()
{
Activity activity = new Activity();
activity.setTransactionStart(new Date());
activity.setUserId(authService.getAuthenticatedUserId());
activityRepository.save(activity);
return activity;
}
@Transactional
public void endTransaction(Activity activity)
{
activity.setTransactionEnd(new Date());
activityRepository.save(activity);
}
}
所有实体的基实体类(Activity 除外):
@MappedSuperclass
@Getter @Setter
public class BaseEntity
{
@Basic
@Column(name = "transaction_id")
private Long transactionId;
@PrePersist
@PreUpdate
public void setupTransaction ()
{
ThreadLocal<Activity> transaction = BaseService.transaction;
Activity activity = transaction.get();
long transactionId = activity.getTransactionId();
setTransactionId(transactionId);
}
}
服务示例:
@Service
public class OrganizationService extends BaseService
{
private final OrgUserRepository orgUserRepository;
private final UserService userService;
@Autowired
public OrganizationService(ActivityService activityService,
OrgUserRepository orgUserRepository,
UserService userService,
ApplicationEventPublisher applicationEventPublisher)
{
super(activityService, applicationEventPublisher);
this.orgUserRepository = orgUserRepository;
this.userService = userService;
}
@Transactional
public OrgUser save(User user, OrgUser orgUser)
{
return (OrgUser) executeWithinActivity(activity ->
{
orgUser.setUser(userService.save(user));
return orgUserRepository.save(orgUser);
});
}
}
UserService 也将扩展 BaseService 并且 save(OrgUser) 方法也将执行WithinActivity。
最后,提交侦听器:
@Component
public class AfterCommitListener
{
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void doAfterTxComplete(BaseService.TransactionEvent event)
{
BaseService.transaction.remove();
}
}
推荐阅读
- go - 将数组索引传递给模板
- javascript - 如何使用正则表达式解析 Ajax/Jquery 响应数据?
- c# - 创建一个小地图但是如何使原始面板和小地图面板的坐标相互同步?
- javascript - 如何将数据从我的节点脚本发送到客户端脚本中的函数
- angular - 试图为嵌入目的抓取站点数据而 CORS 会妨碍您吗?
- c# - 并行处理是否有助于在 c# 中更快地执行 async-await 代码?
- react-native - 获取下一页数据时出现平面列表错误
- python - 如何切出某些行和列,然后只取 Dataframe Python 中的剩余数据?
- python - 如何将十六进制字符串转换为列表中的十六进制值块?
- angular - 手动将 Angular 应用部署到 Azure 应用服务