JPA - Rückgabe einer automatisch generierten ID nach persist ()

113

Ich verwende JPA (EclipseLink) und Spring. Angenommen, ich habe eine einfache Entität mit einer automatisch generierten ID:

@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;

     // ...
}

In meiner DAO-Klasse habe ich eine Einfügemethode, die persist()diese Entität aufruft . Ich möchte, dass die Methode die generierte ID für die neue Entität zurückgibt, aber wenn ich sie teste, wird sie 0stattdessen zurückgegeben.

public class ABCDao {
    @PersistenceContext
    EntityManager em;

    @Transactional(readOnly=false)
    public int insertABC(ABC abc) {
         em.persist(abc);
         // I WANT TO RETURN THE AUTO-GENERATED ID OF abc
         // HOW CAN I DO IT?
         return abc.id; // ???
    }
}

Ich habe auch eine Serviceklasse, die das DAO umschließt, wenn das einen Unterschied macht:

public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}
sura2k
quelle
Ähnliches kann auf stackoverflow.com/q/3328813/366964
Nayan Wadekar
Danke für die Antworten. Und als knifflige Lösung (keine JPA) können wir eine andere eindeutige ID wie den Unix-Zeitstempel verwenden.
sura2k
1
Mögliches Duplikat von Wann setzt die JPA einen @GeneratedValue @Id
Raedwald am

Antworten:

184

Die ID wird garantiert nur zur Spülzeit generiert. Durch das Fortbestehen einer Entität wird sie nur an den Persistenzkontext "angehängt". Spülen Sie also entweder den Entitätsmanager explizit:

em.persist(abc);
em.flush();
return abc.getId();

oder geben Sie die Entität selbst anstelle ihrer ID zurück. Wenn die Transaktion endet, erfolgt der Flush, und Benutzer der Entität außerhalb der Transaktion sehen somit die generierte ID in der Entität.

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}
JB Nizet
quelle
10
NB: Dies muss das ID-Feld mit @GeneratedValue- was auch immer das beinhaltet
Mr_and_Mrs_D
Können Sie bitte die Probleme bei dem Versuch erklären, dies mit der zusammengesetzten ID stackoverflow.com/questions/31362100/…
bl3e
Gibt es eine Leistungsminderung (oder andere negative Auswirkungen) beim manuellen Spülen nach dem Fortbestehen?
Craig Otis
3
Ja, es gibt: unnötigen Roundtrip zur Datenbank, wenn die Transaktion zurückgesetzt wird, mögliche Ausnahmen, wenn sich die persistierte Entität (oder andere gelöschte Entitäten) noch nicht in einem gültigen Zustand befindet. Ein Sequenz- oder UUID-Generator ist einfacher und effizienter und weist diese Probleme nicht auf, da die ID generiert und zugewiesen wird, bevor die Entität in die Datenbank geschrieben wird.
JB Nizet
1
@JBNizet, müssen Sie die Instanz zurückgeben oder ist die übergebene Referenz noch gültig? Ich meine, insertABCerstellt ein neues Objekt? Oder das alte modifizieren?
Ryvantage
13
@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;   
}

Überprüfen Sie, ob die @ GenerationValue-Notation in Ihrer Entitätsklasse vorhanden ist. Dadurch wird JPA über das automatisch generierte Verhalten Ihrer Entitätseigenschaft informiert

utkal patel
quelle
4

So habe ich es gemacht:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();
Koray Tugay
quelle
Es funktioniert nicht, wenn Null als Rückgabe angegeben wird, nachdem die Daten in der Tabelle gespeichert wurden. Entweder funktioniert die Aktualisierung in diesem Fall nicht. Was soll ich dafür tun? Bitte schlagen Sie einen Weg vor ... Danke
Vikrant Kashyap
1
@ VikrantKashyap Bitte poste eine neue Frage mit kleinem Code und erwähne mich, damit ich einen Blick darauf werfen kann.
Koray Tugay
2

Sie können auch GenerationType.TABLE anstelle von IDENTITY verwenden, die erst nach dem Einfügen verfügbar ist.

James
quelle
2
Nur ein Wort der Vorsicht. Als ich GenerationType.TABLE ausprobierte, erstellte es eine separate Tabelle mit dem Namen
hibernate_sequences
0

Eine weitere mit 4.0 kompatible Option:

Bevor Sie die Änderungen festschreiben, können Sie die neuen CayenneDataObjectObjekte aus der dem Kontext zugeordneten Sammlung wie folgt wiederherstellen :

CayenneDataObject dataObjectsCollection = (CayenneDataObject)cayenneContext.newObjects();

Greifen Sie dann auf die ObjectIdfür jeden in der Sammlung zu, wie:

ObjectId objectId = dataObject.getObjectId();

Schließlich können Sie unter den Werten iterieren, wobei die generierte ID normalerweise der erste der Werte (für einen einzelnen Spaltenschlüssel) in der von zurückgegebenen Map getIdSnapshot()ist. Sie enthält auch die Spaltennamen, die der PK zugeordnet sind als Schlüssel:

objectId.getIdSnapshot().values()
emecas
quelle
0

So habe ich es gemacht. Du kannst es versuchen

    public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}
Anh Khoa
quelle
-3
em.persist(abc);
em.refresh(abc);
return abc;
Andrey
quelle
Diese Methode hat bei mir nicht funktioniert. Erhielt diesen Fehler: javax.persistence.PersistenceException: org.hibernate.HibernateException: Diese Instanz existiert noch nicht als Zeile in der Datenbank]
rtcarlson
@rtcarlson, ja das wird nicht funktionieren. Wenn Sie ein neues Objekt erstellen, benötigen Sie dies em.flush()nicht em.refresh(abc).
Ibrahim Dauda