Spring JPA ID транзакции - PullRequest
       4

Spring JPA ID транзакции

0 голосов
/ 25 марта 2020

Я добавил атрибут ко всем своим сущностям - идентификатор транзакции, который представляет собой значение, сгенерированное последовательностью, которое я увеличиваю один раз в каждой транзакции.

Я также сохраняю идентификатор транзакции с пользователем и временем начала / окончания поэтому у меня есть контрольный журнал для каждого изменения в базе данных.

Каков наилучший способ хранения полного графа, когда я в основном хочу применить идентификатор транзакции к тем объектам, которые на самом деле грязные?

Я могу поместить @PrePersist и @PreUpdate в столбец идентификатора транзакции, но как мне получить значение для текущего идентификатора транзакции? Есть ли способ сохранить и получить значение объекта транзакции или другого контроллера JPA? Нужно ли использовать решение ThreadLocal?

1 Ответ

0 голосов
/ 30 марта 2020

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