Хорошо, вот что я сделал. Кажется, он работает во всех случаях использования, хотя я не проводил никакого тестирования производительности и т. Д. c. Если кто-то видит что-то, что может быть неоптимальным или может не сработать в определенных ситуациях, укажите это.
Вот базовый класс обслуживания, который должны расширяться всеми реализациями @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 - это сущность, представляющая сохраненный идентификатор транзакции:
@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();
}
}