Sekwencja hibernacji JPA (bez identyfikatora)

138

Czy można użyć sekwencji DB dla jakiejś kolumny, która nie jest identyfikatorem / nie jest częścią identyfikatora złożonego ?

Używam hibernacji jako dostawcy jpa i mam tabelę, która ma kilka kolumn, w których są generowane wartości (przy użyciu sekwencji), chociaż nie są one częścią identyfikatora.

Chcę użyć sekwencji do utworzenia nowej wartości dla jednostki, w której kolumna sekwencji NIE jest (częścią) klucza podstawowego:

@Entity
@Table(name = "MyTable")
public class MyEntity {

    //...
    @Id //... etc
    public Long getId() {
        return id;
    }

   //note NO @Id here! but this doesn't work...
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
    @SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
    @Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
    public Long getMySequencedValue(){
      return myVal;
    }

}

Wtedy, kiedy to zrobię:

em.persist(new MyEntity());

identyfikator zostanie wygenerowany, ale mySequenceValwłaściwość zostanie również wygenerowana przez mojego dostawcę JPA.

Dla jasności: chcę, aby Hibernate generowało wartość dla mySequencedValuenieruchomości. Wiem, że Hibernate może obsługiwać wartości generowane przez bazę danych, ale nie chcę używać wyzwalacza ani niczego innego niż sam Hibernate, aby wygenerować wartość dla mojej właściwości. Jeśli Hibernate może generować wartości dla kluczy podstawowych, dlaczego nie może generować prostej właściwości?

Miguel Ping
źródło

Odpowiedzi:

76

Szukając odpowiedzi na ten problem, natknąłem się na ten link

Wygląda na to, że Hibernate / JPA nie jest w stanie automatycznie utworzyć wartości dla twoich właściwości innych niż id. @GeneratedValueAdnotacja jest używana tylko w połączeniu z @Iddo tworzenia automatycznych numerów.

@GeneratedValueAdnotacja tylko mówi Hibernate, że baza danych jest generowanie tej samej wartości.

Rozwiązaniem (lub obejściem) sugerowanym na tym forum jest utworzenie oddzielnej encji z wygenerowanym identyfikatorem, coś takiego:

@Jednostka
public class GeneralSequenceNumber {
  @ID
  @GeneratedValue (...)
  prywatny numer długi;
}

@Jednostka 
public class MyEntity {
  @ID ..
  prywatny długi identyfikator;

  @Jeden na jednego(...)
  prywatny GeneralSequnceNumber myVal;
}
Morten Berg
źródło
Z dokumentu java @GeneratedValue: „Adnotację GeneratedValue można zastosować do właściwości klucza podstawowego lub pola encji lub zmapowanej nadklasy w połączeniu z adnotacją Id”
Kariem,
11
Okazało się, że @Column (columnDefinition = "serial") działa idealnie, ale tylko dla PostgreSQL. Dla mnie to było idealne rozwiązanie, ponieważ drugi podmiot to opcja „brzydka”
Sergey Vedernikov
@SergeyVedernikov, który był niezwykle pomocny. Czy mógłbyś zamieścić to jako oddzielną odpowiedź? To rozwiązało mój problem bardzo prosto i skutecznie.
Matt Ball
@MattBall Wysłałem to jako osobną odpowiedź :) stackoverflow.com/a/10647933/620858
Sergey Vedernikov
1
Otworzyłem propozycję, aby zezwolić @GeneratedValuena pola, które nie mają identyfikatora. Zagłosuj na dołączenie do wersji 2.2 java.net/jira/browse/JPA_SPEC-113
Petar Tahchiev
44

Okazało się, że @Column(columnDefinition="serial")działa idealnie, ale tylko dla PostgreSQL. Dla mnie to było idealne rozwiązanie, bo druga rzecz to „brzydka” opcja.

Sergey Vedernikov
źródło
Cześć, potrzebowałbym wyjaśnienia. Czy mógłbyś powiedzieć mi więcej, proszę?
Emaborsa
2
@Emaborsa columnDefinition=Bit w zasadzie mówi Hiberate, aby nie próbował generować definicji kolumny i zamiast tego używał podanego tekstu. Zasadniczo twój DDL dla kolumny będzie dosłownie po prostu nazwą + columnDefinition. W tym przypadku (PostgreSQL) mycolumn serialjest poprawną kolumną w tabeli.
Patrick
7
Odpowiednikiem MySQL jest@Column(columnDefinition = "integer auto_increment")
Richard Kennard
2
Czy to automatycznie generuje swoją wartość? Próbowałem utrwalić jednostkę z taką definicją pola, ale nie wygenerowała ona wartości. wyrzuciła wartość null w kolumnie <kolumna> narusza ograniczenie inne niż zerowe
KyelJmD
7
Kiedyś @Column(insertable = false, updatable = false, columnDefinition="serial")zapobiegałem hibernacji przed próbą wstawiania wartości null lub aktualizowania pola. Następnie musisz ponownie wysłać zapytanie do bazy danych, aby uzyskać wygenerowany identyfikator po wstawieniu, jeśli chcesz go od razu użyć.
Robert Di Paolo
20

Wiem, że jest to bardzo stare pytanie, ale zostało pokazane po raz pierwszy w wynikach, a jpa bardzo się zmienił od czasu pytania.

Właściwy sposób na zrobienie tego teraz polega na zastosowaniu @Generatedadnotacji. Możesz zdefiniować sekwencję, ustawić wartość domyślną w kolumnie na tę sekwencję, a następnie zmapować kolumnę jako:

@Generated(GenerationTime.INSERT)
@Column(name = "column_name", insertable = false)
Rumal
źródło
1
To nadal wymaga wygenerowania wartości przez bazę danych, co tak naprawdę nie odpowiada na pytanie. W przypadku baz danych Oracle starszych niż 12c nadal trzeba byłoby napisać wyzwalacz bazy danych, aby wygenerować wartość.
Bernie,
9
jest to również adnotacja Hibernate, a nie JPA.
caarlos0
14

Hibernate zdecydowanie to obsługuje. Z dokumentów:

„Właściwości generowane to właściwości, których wartości są generowane przez bazę danych. Zwykle aplikacje Hibernate potrzebowały do ​​odświeżania obiektów zawierających właściwości, dla których baza danych generowała wartości. Oznaczanie właściwości jako wygenerowanych pozwala jednak aplikacji przekazać tę odpowiedzialność Hibernate. Zasadniczo za każdym razem, gdy Hibernate wydaje instrukcję SQL INSERT lub UPDATE dla jednostki, która ma zdefiniowane wygenerowane właściwości, natychmiast wydaje następnie polecenie wyboru w celu pobrania wygenerowanych wartości ”.

W przypadku właściwości wygenerowanych tylko przy wstawianiu mapowanie właściwości (.hbm.xml) wyglądałoby następująco:

<property name="foo" generated="insert"/>

Dla właściwości wygenerowanych przy wstawianiu i aktualizowaniu mapowania właściwości (.hbm.xml) wyglądałoby następująco:

<property name="foo" generated="always"/>

Niestety nie znam JPA, więc nie wiem, czy ta funkcja jest ujawniona przez JPA (podejrzewam, że może nie)

Alternatywnie powinieneś móc wykluczyć właściwość z operacji wstawiania i aktualizacji, a następnie „ręcznie” wywołać session.refresh (obj); po wstawieniu / zaktualizowaniu go, aby załadować wygenerowaną wartość z bazy danych.

W ten sposób można wykluczyć właściwość z używania w instrukcjach insert i update:

<property name="foo" update="false" insert="false"/>

Ponownie, nie wiem, czy JPA ujawnia te funkcje Hibernacji, ale Hibernate je obsługuje.

alasdairg
źródło
1
Adnotacja @Generated odpowiada powyższej konfiguracji XML. Więcej szczegółów znajdziesz w tej sekcji dokumentacji dotyczącej hibernacji .
Eric
8

W ramach kontynuacji oto, jak to działa:

@Override public Long getNextExternalId() {
    BigDecimal seq =
        (BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
    return seq.longValue();
}
Paweł
źródło
Wariant z Hibernate 4.2.19 i Oracle: SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
Aaron
6

Naprawiłem generowanie UUID (lub sekwencji) w Hibernate za pomocą @PrePersistadnotacji:

@PrePersist
public void initializeUUID() {
    if (uuid == null) {
        uuid = UUID.randomUUID().toString();
    }
}
Matroska
źródło
5

Chociaż jest to stary wątek, chcę podzielić się moim rozwiązaniem i mam nadzieję, że otrzymam na ten temat opinię. Ostrzegam, że testowałem to rozwiązanie tylko z moją lokalną bazą danych w jakimś przypadku testowym JUnit. Jak dotąd nie jest to wydajna funkcja.

Rozwiązałem ten problem, wprowadzając niestandardową adnotację o nazwie Sekwencja bez właściwości. To tylko znacznik dla pól, którym należy przypisać wartość z sekwencji zwiększonej.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sequence
{
}

Używając tej adnotacji, oznaczyłem moje encje.

public class Area extends BaseEntity implements ClientAware, IssuerAware
{
    @Column(name = "areaNumber", updatable = false)
    @Sequence
    private Integer areaNumber;
....
}

Aby zachować niezależność bazy danych rzeczy, wprowadziłem jednostkę o nazwie SequenceNumber, która przechowuje bieżącą wartość sekwencji i rozmiar przyrostu. Wybrałem className jako unikalny klucz, więc każda klasa encji otrzyma własną sekwencję.

@Entity
@Table(name = "SequenceNumber", uniqueConstraints = { @UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
    @Id
    @Column(name = "className", updatable = false)
    private String className;

    @Column(name = "nextValue")
    private Integer nextValue = 1;

    @Column(name = "incrementValue")
    private Integer incrementValue = 10;

    ... some getters and setters ....
}

Ostatnim krokiem i najtrudniejszym jest PreInsertListener, który obsługuje przypisanie numeru sekwencji. Zauważ, że użyłem wiosny jako pojemnika na fasolę.

@Component
public class SequenceListener implements PreInsertEventListener
{
    private static final long serialVersionUID = 7946581162328559098L;
    private final static Logger log = Logger.getLogger(SequenceListener.class);

    @Autowired
    private SessionFactoryImplementor sessionFactoryImpl;

    private final Map<String, CacheEntry> cache = new HashMap<>();

    @PostConstruct
    public void selfRegister()
    {
        // As you might expect, an EventListenerRegistry is the place with which event listeners are registered
        // It is a service so we look it up using the service registry
        final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);

        // add the listener to the end of the listener chain
        eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
    }

    @Override
    public boolean onPreInsert(PreInsertEvent p_event)
    {
        updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());

        return false;
    }

    private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
    {
        try
        {
            List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);

            if (!fields.isEmpty())
            {
                if (log.isDebugEnabled())
                {
                    log.debug("Intercepted custom sequence entity.");
                }

                for (Field field : fields)
                {
                    Integer value = getSequenceNumber(p_entity.getClass().getName());

                    field.setAccessible(true);
                    field.set(p_entity, value);
                    setPropertyState(p_state, p_propertyNames, field.getName(), value);

                    if (log.isDebugEnabled())
                    {
                        LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
                    }
                }
            }
        }
        catch (Exception e)
        {
            log.error("Failed to set sequence property.", e);
        }
    }

    private Integer getSequenceNumber(String p_className)
    {
        synchronized (cache)
        {
            CacheEntry current = cache.get(p_className);

            // not in cache yet => load from database
            if ((current == null) || current.isEmpty())
            {
                boolean insert = false;
                StatelessSession session = sessionFactoryImpl.openStatelessSession();
                session.beginTransaction();

                SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);

                // not in database yet => create new sequence
                if (sequenceNumber == null)
                {
                    sequenceNumber = new SequenceNumber();
                    sequenceNumber.setClassName(p_className);
                    insert = true;
                }

                current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
                cache.put(p_className, current);
                sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());

                if (insert)
                {
                    session.insert(sequenceNumber);
                }
                else
                {
                    session.update(sequenceNumber);
                }
                session.getTransaction().commit();
                session.close();
            }

            return current.next();
        }
    }

    private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
    {
        for (int i = 0; i < propertyNames.length; i++)
        {
            if (propertyName.equals(propertyNames[i]))
            {
                propertyStates[i] = propertyState;
                return;
            }
        }
    }

    private static class CacheEntry
    {
        private int current;
        private final int limit;

        public CacheEntry(final int p_limit, final int p_current)
        {
            current = p_current;
            limit = p_limit;
        }

        public Integer next()
        {
            return current++;
        }

        public boolean isEmpty()
        {
            return current >= limit;
        }
    }
}

Jak widać z powyższego kodu, odbiornik użył jednej instancji SequenceNumber na klasę jednostki i rezerwuje kilka numerów sekwencyjnych zdefiniowanych przez jednostkę IncrementValue jednostki SequenceNumber. Jeśli zabraknie numerów sekwencyjnych, ładuje jednostkę SequenceNumber dla klasy docelowej i rezerwuje wartości IncrementValue dla następnych wywołań. W ten sposób nie muszę przesyłać zapytań do bazy danych za każdym razem, gdy potrzebna jest wartość sekwencji. Zwróć uwagę na StatelessSession, który jest otwierany w celu zarezerwowania następnego zestawu numerów sekwencyjnych. Nie można użyć tej samej sesji, w której jednostka docelowa jest obecnie utrwalana, ponieważ spowodowałoby to ConcurrentModificationException w EntityPersister.

Mam nadzieję, że to komuś pomoże.

Sebastian Götz
źródło
5

Jeśli używasz postgresql
A ja używam w butach wiosennych 1.5.6

@Column(columnDefinition = "serial")
@Generated(GenerationTime.INSERT)
private Integer orderID;
Sulaymon Hursanov
źródło
1
U mnie też to zadziałało, używam buta sprężynowego 2.1.6.RELEASE, Hibernate 5.3.10.Final, Oprócz tego, co już wskazałem, musiałem stworzyć zabezpieczenie seq_orderi odnieść się do pola, nextval('seq_order'::regclass)
OJVM,
3

Działam w takiej samej sytuacji jak Ty i nie znalazłem też żadnych poważnych odpowiedzi, czy w zasadzie możliwe jest wygenerowanie właściwości bez identyfikatora za pomocą JPA, czy nie.

Moim rozwiązaniem jest wywołanie sekwencji za pomocą natywnego zapytania JPA w celu ręcznego ustawienia właściwości przed jej utrwaleniem.

Nie jest to satysfakcjonujące, ale na razie działa jako obejście.

Mario


źródło
2

Znalazłem tę szczególną uwagę w sesji 9.1.9 Adnotacja GeneratedValue ze specyfikacji JPA: „[43] Aplikacje przenośne nie powinny używać adnotacji GeneratedValue w innych trwałych polach lub właściwościach”. Zakładam więc, że nie jest możliwe automatyczne generowanie wartości dla wartości kluczy innych niż podstawowe, przynajmniej przy użyciu prostego JPA.

Gustavo Orair
źródło
1

Wygląda na to, że wątek jest stary, chciałem tylko dodać tutaj moje rozwiązanie (wiosną przy użyciu AspectJ - AOP).

Rozwiązaniem jest utworzenie niestandardowej adnotacji @InjectSequenceValuew następujący sposób.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectSequenceValue {
    String sequencename();
}

Teraz możesz dodać adnotację do dowolnego pola w encji, tak aby wartość pola bazowego (Long / Integer) została wstrzyknięta w czasie wykonywania przy użyciu następnej wartości sekwencji.

Opisuj w ten sposób.

//serialNumber will be injected dynamically, with the next value of the serialnum_sequence.
 @InjectSequenceValue(sequencename = "serialnum_sequence") 
  Long serialNumber;

Do tej pory zaznaczyliśmy pole, które musimy wstrzyknąć wartość sekwencji, więc przyjrzymy się, jak wstrzyknąć wartość sekwencji do zaznaczonych pól, odbywa się to poprzez utworzenie cięcia punktowego w AspectJ.

Wstrzyknięcie wyzwolimy tuż przed wykonaniem save/persistmetody, co odbywa się w poniższej klasie.

@Aspect
@Configuration
public class AspectDefinition {

    @Autowired
    JdbcTemplate jdbcTemplate;


    //@Before("execution(* org.hibernate.session.save(..))") Use this for Hibernate.(also include session.save())
    @Before("execution(* org.springframework.data.repository.CrudRepository.save(..))") //This is for JPA.
    public void generateSequence(JoinPoint joinPoint){

        Object [] aragumentList=joinPoint.getArgs(); //Getting all arguments of the save
        for (Object arg :aragumentList ) {
            if (arg.getClass().isAnnotationPresent(Entity.class)){ // getting the Entity class

                Field[] fields = arg.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(InjectSequenceValue.class)) { //getting annotated fields

                        field.setAccessible(true); 
                        try {
                            if (field.get(arg) == null){ // Setting the next value
                                String sequenceName=field.getAnnotation(InjectSequenceValue.class).sequencename();
                                long nextval=getNextValue(sequenceName);
                                System.out.println("Next value :"+nextval); //TODO remove sout.
                                field.set(arg, nextval);
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }
    }

    /**
     * This method fetches the next value from sequence
     * @param sequence
     * @return
     */

    public long getNextValue(String sequence){
        long sequenceNextVal=0L;

        SqlRowSet sqlRowSet= jdbcTemplate.queryForRowSet("SELECT "+sequence+".NEXTVAL as value FROM DUAL");
        while (sqlRowSet.next()){
            sequenceNextVal=sqlRowSet.getLong("value");

        }
        return  sequenceNextVal;
    }
}

Teraz możesz dodać adnotację do dowolnej jednostki, jak poniżej.

@Entity
@Table(name = "T_USER")
public class UserEntity {

    @Id
    @SequenceGenerator(sequenceName = "userid_sequence",name = "this_seq")
    @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "this_seq")
    Long id;
    String userName;
    String password;

    @InjectSequenceValue(sequencename = "serialnum_sequence") // this will be injected at the time of saving.
    Long serialNumber;

    String name;
}
Subin Chalil
źródło
0

„Nie chcę używać wyzwalacza ani niczego innego niż sama Hibernacja do generowania wartości dla mojej nieruchomości”

W takim przypadku, co powiesz na utworzenie implementacji UserType, która generuje wymaganą wartość i skonfigurowanie metadanych tak, aby używały tego UserType do trwałości właściwości mySequenceVal?

alasdairg
źródło
0

To nie to samo, co użycie sekwencji. Używając sekwencji, niczego nie wstawiasz ani nie aktualizujesz. Po prostu pobierasz następną wartość sekwencji. Wygląda na to, że hibernacja go nie obsługuje.

kammy
źródło
0

Jeśli masz kolumnę typu UNIQUEIDENTIFIER i domyślną generację potrzebną do wstawiania, ale kolumna nie jest PK

@Generated(GenerationTime.INSERT)
@Column(nullable = false , columnDefinition="UNIQUEIDENTIFIER")
private String uuidValue;

W db będziesz mieć

CREATE TABLE operation.Table1
(
    Id         INT IDENTITY (1,1)               NOT NULL,
    UuidValue  UNIQUEIDENTIFIER DEFAULT NEWID() NOT NULL)

W takim przypadku nie zdefiniujesz generatora dla wartości, której potrzebujesz (będzie to automatycznie dzięki columnDefinition="UNIQUEIDENTIFIER"). To samo możesz wypróbować dla innych typów kolumn

Artem Novitskii
źródło
0

Znalazłem obejście tego problemu w bazach danych MySql przy użyciu @PostConstruct i JdbcTemplate w aplikacji Spring. Może to być wykonalne z innymi bazami danych, ale przypadek użycia, który przedstawię, jest oparty na moich doświadczeniach z MySql, ponieważ używa on auto_increment.

Po pierwsze, próbowałem zdefiniować kolumnę jako auto_increment przy użyciu właściwości ColumnDefinition adnotacji @Column, ale nie działała ona, ponieważ kolumna musiała być kluczem, aby była automatycznie inkrementalna, ale najwyraźniej kolumna nie została zdefiniowana jako indeks, dopóki nie został zdefiniowany, powodując zakleszczenie.

W tym miejscu przyszedł mi do głowy pomysł stworzenia kolumny bez definicji auto_increment i dodania jej po utworzeniu bazy danych. Jest to możliwe dzięki adnotacji @PostConstruct, która powoduje wywołanie metody zaraz po zainicjowaniu komponentów bean przez aplikację, w połączeniu z metodą update JdbcTemplate.

Kod wygląda następująco:

W mojej jednostce:

@Entity
@Table(name = "MyTable", indexes = { @Index(name = "my_index", columnList = "mySequencedValue") })
public class MyEntity {
    //...
    @Column(columnDefinition = "integer unsigned", nullable = false, updatable = false, insertable = false)
    private Long mySequencedValue;
    //...
}

W klasie PostConstructComponent:

@Component
public class PostConstructComponent {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void makeMyEntityMySequencedValueAutoIncremental() {
        jdbcTemplate.update("alter table MyTable modify mySequencedValue int unsigned auto_increment");
    }
}
Ignacio Velásquez Lagos
źródło
0

Chcę zapewnić alternatywę obok przyjętego rozwiązania @Morten Berg, które działało lepiej dla mnie.

Takie podejście pozwala zdefiniować pole z faktycznie pożądanym Numbertypem - Longw moim przypadku - zamiast GeneralSequenceNumber. Może to być przydatne, np. Do serializacji JSON (de-).

Wadą jest to, że wymaga trochę więcej narzutu na bazę danych.


Najpierw potrzebujemy elementu, ActualEntityw którym chcemy automatycznie zwiększać generatedtyp Long:

// ...
@Entity
public class ActualEntity {

    @Id 
    // ...
    Long id;

    @Column(unique = true, updatable = false, nullable = false)
    Long generated;

    // ...

}

Następnie potrzebujemy jednostki pomocniczej Generated. Umieściłem go obok pakietu prywatnego ActualEntity, aby zachować szczegół implementacji pakietu:

@Entity
class Generated {

    @Id
    @GeneratedValue(strategy = SEQUENCE, generator = "seq")
    @SequenceGenerator(name = "seq", initialValue = 1, allocationSize = 1)
    Long id;

}

Na koniec potrzebujemy miejsca, w którym można podłączyć się tuż przed zapisaniem pliku ActualEntity. Tam tworzymy i utrzymujemy Generatedinstancję. To następnie zapewnia wygenerowaną sekwencję bazy danych idtypu Long. Używamy tej wartości, pisząc ją do ActualEntity.generated.

W moim przypadku zaimplementowałem to za pomocą Spring Data REST @RepositoryEventHandler, który jest wywoływany tuż przed ActualEntityutrwaleniem get. Powinien pokazywać zasadę:

@Component
@RepositoryEventHandler
public class ActualEntityHandler {

    @Autowired
    EntityManager entityManager;

    @Transactional
    @HandleBeforeCreate
    public void generate(ActualEntity entity) {
        Generated generated = new Generated();

        entityManager.persist(generated);
        entity.setGlobalId(generated.getId());
        entityManager.remove(generated);
    }

}

Nie testowałem tego w prawdziwej aplikacji, więc ciesz się ostrożnie.

aboger
źródło
-1

Byłem w takiej sytuacji jak Ty (sekwencja JPA / Hibernacja dla pola innego niż @Id) i skończyło się na utworzeniu wyzwalacza w moim schemacie db, który dodaje unikalny numer sekwencyjny przy wstawianiu. Po prostu nigdy nie działało z JPA / Hibernate

Frederic Morin
źródło
-1

Po spędzeniu godzin pomogło mi to w rozwiązaniu problemu:

W przypadku Oracle 12c:

ID NUMBER GENERATED as IDENTITY

Dla H2:

ID BIGINT GENERATED as auto_increment

Wykonaj również:

@Column(insertable = false)
Wiosna
źródło