HIbernate проблема с Oracle Trigger для генерации идентификатора из последовательности - PullRequest
8 голосов
/ 04 ноября 2011

У нас есть триггер перед вставкой, который получает следующее значение из последовательности.Когда объект сохраняется с помощью метода save (), hibernate получает значение из последовательности и добавляет его к объекту.и когда транзакция фиксируется из сервисного уровня Spring, значение ID снова увеличивается в базе данных.как избежать получения nextval (), если объект уже имеет идентификатор ..

Вот что я пытаюсь сделать ..

UserDao

public User saveUser(User user){
      session.getCurrentSession.save(user);//line2
      return user;//line3  
 }

UserService

public void saveUserAndWriteToAudit(User user, UserAudit userAudit){
  userDao.saveUser(user);//line1
  userAudit.setUserId(user.getId);//line4
  userAudit.saveUserAudit(userAudit);//line5
}

И пользовательский класс

 @Entity
  public class User{

     @Id
     @GeneratedValue(strategy=GenerationType.AUTO, generator="a1")
     @SequenceGenerator(name="a1", sequenceName="usersequence")
     private Long id;
     /////////////////
 }

Когда курсор достигает строки1 и строки2, пользовательский объект имеет нулевое значение в атрибуте id.после строки 2 он имеет следующее значение из последовательности - скажем, 1. в строке 4 я добавил идентификатор пользователя = 1 в объект useraudit. когда транзакция фиксируется после строки 5, 2 вставляется в столбец идентификатора пользователя и 1 - в столбец userAd пользователя UserAudit.Это бесполезно для меня :( Как мне избежать этой проблемы? Спасибо!

Ответы [ 4 ]

10 голосов
/ 04 ноября 2011

Просто обновите свой триггер, чтобы он срабатывал только тогда, когда ему не присвоен идентификатор.

create or replace
trigger sa.my_trigger
before insert on sa.my_table
for each row
when (new.id is null)
begin
   select sa.my_sequence.nextval
    into :new.id
    from dual;
end;
5 голосов
/ 18 декабря 2012

Приведенное выше решение отлично, оно избавило меня от многих проблем по этой проблеме.

Моя единственная неприятность в том, что она открывает дверь для пользователя / кодов, чтобы вставить любое значение ID без фактического запроса последовательности.

Я обнаружил, что следующее решение также работает. Это позволяет Hibernate найти максимальный идентификатор и увеличивать его каждый раз, когда выполняется оператор вставки. Но когда он попадает в базу данных, идентификатор игнорируется и заменяется идентификатором, сгенерированным триггером, поэтому уникальность в кластерной проблеме

    @Id
    @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;

Самым большим недостатком является то, что @GenericGenerator - это аннотация Hibernate, поэтому вы теряете переносимость JPA. Программистам также не ясно, что этот идентификатор на самом деле связан с последовательностью, хотя на самом деле это одно из самых тесно связанных решений, использующих последовательность.

0 голосов
/ 26 ноября 2013
create or replace
trigger sa.my_trigger
before insert on sa.my_table
for each row
when (new.id is null)
begin
   select sa.my_sequence.nextval
    into :new.id
    from dual;
end;

это что-то действительно не так в мире баз данных.

Учитывая приведенное выше решение, допустим, что sa.my_sequence.nextval равен 51, и ваша среда гибернации будет работать без проблем. Но если кто-то сделает прямую вставку jdbc с вашим значением первичного ключа как 66, переопределяя последовательность (скажем, текущее значение как 52), триггер просто вставит.

Реальная проблема заключается в увеличении значения последовательности до 66, что вызовет исключение в триггере, заканчивающемся сбросом значения последовательности. Это действительно заканчивается плохой структурой схемы со стороны базы данных.

0 голосов
/ 04 ноября 2011

В идеале вы должны удалить ПЕРЕД ВСТАВКОЙ ТРИГГЕРОМ. Если вы этого не сделаете, Hibernate не сможет узнать первичный ключ, и ему действительно нужна эта информация. Если вы не заботитесь об объекте после INSERT, это может быть нормально (кэш 2-го уровня все еще остается проблемой), но если вам нужно использовать его сразу, это настоящая проблема. В последнем случае вы можете попробовать этот неприятный подход:

  1. Скажите Hibernate, что вы сами управляете первичным ключом.
  2. Создайте новый объект и поместите произвольное значение в первичный ключ.
  3. Сбросить сеанс Hibernate и выбрать SELECT sequence.CURRVAL (при условии, что имеется только одна INSERT).
  4. Загрузите объект, используя полученное текущее значение последовательности, и не используйте приведенный выше экземпляр.
...