JPA - Restituzione di un ID generato automaticamente dopo persist ()


113

Sto usando JPA (EclipseLink) e Spring. Supponiamo che io abbia un'entità semplice con un ID generato automaticamente:

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

     // ...
}

Nella mia classe DAO, ho un metodo di inserimento che chiama persist()questa entità. Voglio che il metodo restituisca l'ID generato per la nuova entità, ma quando lo provo, restituisce 0invece.

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; // ???
    }
}

Ho anche una classe di servizio che avvolge il DAO, se questo fa la differenza:

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

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}

Uno simile, può fare riferimento stackoverflow.com/q/3328813/366964
Nayan Wadekar

Grazie per le risposte. E come soluzione complicata (non un JPA) possiamo usare un altro id univoco come unix timestamp.
sura2k

Risposte:


184

È garantito che l'ID venga generato solo al momento dello scaricamento. La persistenza di un'entità la rende solo "attaccata" al contesto di persistenza. Quindi, svuota esplicitamente il gestore entità:

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

o restituire l'entità stessa anziché il suo ID. Quando la transazione termina, verrà eseguito il flush e gli utenti dell'entità al di fuori della transazione vedranno quindi l'ID generato nell'entità.

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}

10
NB: questo deve annotare il campo id con @GeneratedValue- qualunque cosa ciò implichi
Mr_and_Mrs_D

U può spiegare i problemi nel tentativo di raggiungere questo obiettivo con composito id stackoverflow.com/questions/31362100/...
bl3e

C'è una penalità nelle prestazioni (o altri effetti negativi) dello scarico manuale dopo la persistenza?
Craig Otis

3
Sì, c'è: roundtrip non necessario al database se la transazione finisce per essere annullata, potenziali eccezioni se l'entità persistente (o altre entità scaricate) non è ancora in uno stato valido. Un generatore di sequenza o uuid è più semplice ed efficiente e non presenta questi problemi perché l'ID viene generato e assegnato prima che l'entità venga scritta nel database.
JB Nizet

1
@JBNizet, devi restituire l'istanza o il riferimento passato è ancora valido? Voglio dire, insertABCcrea un nuovo oggetto? O modificare quello vecchio?
ryvantage

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

controlla che la notazione @GeneratedValue sia presente nella tua classe di entità, per indicare a JPA il comportamento generato automaticamente dalla proprietà dell'entità


4

Ecco come l'ho fatto:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();

Non funziona dando zero come ritorno dopo aver persistito i dati nella tabella. o l'aggiornamento non funziona in questo caso .. Cosa dovrei fare per questo. per favore suggerisci un modo ... Grazie
Vikrant Kashyap

1
@VikrantKashyap Pubblica una nuova domanda con un piccolo codice e menzionami in modo che io possa dare un'occhiata.
Koray Tugay

2

Puoi anche usare GenerationType.TABLE invece di IDENTITY, disponibile solo dopo l'inserimento.


2
Solo una parola di cautela. Quando ho provato GenerationType.TABLE, ha creato una tabella separata denominata hibernate_sequences e ha riavviato la sequenza da 1.
SriSri

0

Un'altra opzione compatibile con 4.0:

Prima di confermare le modifiche, puoi recuperare i nuovi CayenneDataObjectoggetti dalla raccolta associata al contesto, in questo modo:

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

quindi accedi a ObjectIdper ognuno nella raccolta, come:

ObjectId objectId = dataObject.getObjectId();

Infine puoi iterare sotto i valori, dove di solito l'id generato sarà il primo dei valori (per una singola chiave di colonna) nella mappa restituita da getIdSnapshot(), contiene anche i nomi delle colonne associati alla PK come chiave (e):

objectId.getIdSnapshot().values()

0

Ecco come l'ho fatto. Puoi provare

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

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}

-3
em.persist(abc);
em.refresh(abc);
return abc;

Questo metodo non ha funzionato per me. Ho ricevuto questo errore: javax.persistence.PersistenceException: org.hibernate.HibernateException: questa istanza non esiste ancora come riga nel database]
rtcarlson

@rtcarlson, sì, non funzionerà. se stai creando un nuovo oggetto, ciò di cui hai bisogno em.flush()non lo è em.refresh(abc).
Ibrahim Dauda
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.