Триггеры против JPA @PrePersist для создания и обновления временных отметок за и против - PullRequest
9 голосов
/ 09 марта 2011

Я создаю новое веб-приложение и использую Spring, JPA / Hibernate и Postgres. В некоторых из моих таблиц есть столбцы creation_ts и lastupdate_ts, которые представляют собой столбцы отметок времени, которые отслеживают, когда произошла вставка и когда произошло последнее обновление в строке.

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

У меня есть два способа поддерживать эти поля в актуальном состоянии.

Вариант A: использовать триггеры

Это решение, которое у меня есть на данный момент, у меня есть два триггера Postgres, которые запускаются при вставке и обновлении и будут обновлять эти поля. и у меня есть два класса.

@MappedSuperclass
public abstract class PersistableObject
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="pkey")
    private Integer pkey;

    @Version
    @Column(name="version")
    private Integer version;

    public Integer getPkey()
    {
        return this.pkey;
    }

    public Integer getVersion()
    {
        return this.version;
    }
}

а у меня

@MappedSuperclass
public class TimeStampedPersistableObject extends PersistableObject {

    @Column(name = "creation_ts")
    @Temporal(TemporalType.DATE)
    @org.hibernate.annotations.Generated(value = GenerationTime.INSERT)
    private Date    creationTimestamp;

    @Column(name = "update_ts")
    @Temporal(TemporalType.DATE)
    @org.hibernate.annotations.Generated(value = GenerationTime.ALWAYS)
    private Date    updateTimestamp;

    public Date getCreationTimestamp()
    {
        return this.creationTimestamp;
    }

    public Date getUpdateTimestamp()
    {
        return this.updateTimestamp;
    }
}

Вариант B: использовать JPA-слушатели

В этом варианте я бы использовал обработчики JPA, чтобы поддерживать актуальность столбцов меток времени.

Мой вопрос:

Какой из этих двух подходов лучше? Как я вижу, здесь есть мой личный список плюсов и минусов каждого варианта, и мне очень интересно услышать опыт других с этими двумя вариантами.

Вариант А плюсы:

  1. База данных выполняет обновления с помощью триггеров, поэтому нет опасности, что на кластере, работающем в веб-приложении, будет асимметрия часов.
  2. Если приложение не из JPA обращается к базе данных, требование соблюдения этих двух столбцов выполняется.

Вариант A минусы:

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

Варианты B, плюсы:

  1. Меньше ввода при создании DDL
  2. Нет необходимости считывать значения из базы данных после вставки и обновления
  3. Чистые аннотации JPA без специальных спящих аннотаций

Варианты B минусы:

  1. Опасность перекоса часов в кластере
  2. Поля, установленные всякий раз, когда поставщик JPA решает вызвать методы обратного вызова, не предсказуемые

Как вы решите эту проблему для нового приложения, где у вас есть полный контроль над базой данных и кодом Java.

Ответы [ 3 ]

6 голосов
/ 09 марта 2011

Необходимо сделать выбор после вставки и обновить, чтобы прочитать значения, которые триггеры установили на место.

Вы можете использовать INSERT ... RETURNING или UPDATE ... RETURNING, чтобы получить значениякоторые были изменены триггером, поэтому нет необходимости делать еще один SELECT.

Кроме того, я бы сказал, что это зависит от вашей среды.Если приложение является критически важным и с треском провалится, если эти столбцы не будут правильно поддерживаться, то я буду придерживаться триггеров.

Если это только для удобства внешнего интерфейса (и он может корректно обрабатывать конфликты из-за неправильных значений), то подход JPA, вероятно, легче поддерживать.

2 голосов
/ 09 марта 2011

В настоящее время я использую вариант A (со всеми вашими фреймворками и PostgreSQL) следующим образом:

@Column(insertable = false, updatable = false, nullable = false, columnDefinition = "timestamp without time zone default CURRENT_TIMESTAMP")
@Generated(GenerationTime.INSERT)
public Date getCreatedIn() {
    return createdIn;
}

Если вы используете columnDefinition, как написано в коде, вам не придетсявыберите объект еще раз и не пишите код для установки даты на ваших объектах.Я никогда не использовал обратные вызовы JPA, кроме как для подключения инфраструктуры Envers, но я могу сказать, что выглядит слишком много усилий только для установки даты для определенных объектов.

1 голос
/ 09 марта 2011

Опасность перекоса часов в кластере

Я бы сказал, чтобы не беспокоиться об этом. В кластере может произойти сбой, как в базе данных. В надлежащей производственной среде у вас будут собственные NTP-серверы, и ваш кластер будет синхронизироваться с этим.

Я обычно предпочитаю все хранить на Java, поскольку централизованное место для "логики" желательно (для меня). Размещение логики в триггерах допустимо, если у вас есть не-Java-приложения, использующие ее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...