JPA - Renvoi d'un identifiant généré automatiquement après persist ()

113

J'utilise JPA (EclipseLink) et Spring. Disons que j'ai une entité simple avec un identifiant généré automatiquement:

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

     // ...
}

Dans ma classe DAO, j'ai une méthode d'insertion qui appelle persist()cette entité. Je veux que la méthode renvoie l'ID généré pour la nouvelle entité, mais lorsque je la teste, elle renvoie à la 0place.

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

J'ai également une classe de service qui enveloppe le DAO, si cela fait une différence:

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

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}
sura2k
la source
Similaire, peut se référer à stackoverflow.com/q/3328813/366964
Nayan Wadekar
Merci pour les réponses. Et comme solution délicate (pas un JPA), nous pouvons utiliser un autre identifiant unique comme l'horodatage unix.
sura2k
1
duplication possible de Quand le JPA définit-il un @GeneratedValue @Id
Raedwald

Réponses:

184

L'ID est uniquement généré au moment du rinçage. La persistance d'une entité la rend uniquement «attachée» au contexte de persistance. Donc, soit vider explicitement le gestionnaire d'entités:

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

ou renvoyer l'entité elle-même plutôt que son ID. Lorsque la transaction se termine, le vidage se produira et les utilisateurs de l'entité en dehors de la transaction verront ainsi l'ID généré dans l'entité.

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}
JB Nizet
la source
10
NB: cela doit annoter le champ id avec @GeneratedValue- quoi que cela implique
Mr_and_Mrs_D
Pouvez-vous s'il vous plaît expliquer les problèmes en essayant d'y parvenir avec l'identifiant composite stackoverflow.com/questions/31362100/…
bl3e
Y a-t-il une pénalité de performance (ou tout autre effet négatif) du rinçage manuel après avoir persisté?
Craig Otis
3
Oui, il y a: aller-retour inutile vers la base de données si la transaction finit par être annulée, exceptions potentielles si l'entité persistante (ou d'autres entités vidées) n'est pas encore dans un état valide. Un générateur de séquence ou d'uuid est plus simple et plus efficace et ne pose pas ces problèmes car l'ID est généré et attribué avant que l'entité ne soit écrite dans la base de données.
JB Nizet
1
@JBNizet, devez-vous renvoyer l'instance ou la référence passée est-elle toujours valide? Je veux dire, insertABCcrée-t-il un nouvel objet? Ou modifier l'ancien?
ryvantage
13
@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;   
}

vérifiez que la notation @GeneratedValue est présente dans votre classe d'entité. Cela indique à JPA le comportement généré automatiquement par votre propriété d'entité

utkal patel
la source
4

Voici comment je l'ai fait:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();
Koray Tugay
la source
Il ne fonctionne pas en donnant zéro comme retour après avoir conservé les données dans la table. soit le rafraîchissement ne fonctionne pas dans ce cas .. Que dois-je faire pour cela. veuillez suggérer un moyen ... Merci
Vikrant Kashyap
1
@VikrantKashyap S'il vous plaît postez une nouvelle question avec un petit code et mentionnez-moi pour que je puisse y jeter un coup d'œil.
Koray Tugay
2

Vous pouvez également utiliser GenerationType.TABLE au lieu de IDENTITY qui n'est disponible qu'après l'insertion.

James
la source
2
Juste un mot d'avertissement. Quand j'ai essayé GenerationType.TABLE, il a créé une table séparée nommée hibernate_sequences et a redémarré la séquence à partir de 1.
SriSri
0

Une autre option compatible avec 4.0:

Avant de valider les modifications, vous pouvez récupérer le ou les nouveaux CayenneDataObjectobjets de la collection associée au contexte, comme ceci:

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

puis accédez au ObjectIdpour chacun dans la collection, comme:

ObjectId objectId = dataObject.getObjectId();

Enfin, vous pouvez itérer sous les valeurs, où généralement l'identifiant généré sera la première des valeurs (pour une clé de colonne unique) dans la carte renvoyée par getIdSnapshot(), il contient également le ou les noms de colonne associés au PK comme clé (s):

objectId.getIdSnapshot().values()
emecas
la source
0

Voilà comment je l'ai fait. Tu peux essayer

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

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}
Anh Khoa
la source
-3
em.persist(abc);
em.refresh(abc);
return abc;
Andreï
la source
Cette méthode n'a pas fonctionné pour moi. Vous avez cette erreur: javax.persistence.PersistenceException: org.hibernate.HibernateException: cette instance n'existe pas encore sous forme de ligne dans la base de données]
rtcarlson
@rtcarlson, oui cela ne fonctionnera pas. si vous créez un nouvel objet, ce dont vous avez besoin em.flush()ne l' est pas em.refresh(abc).
Ibrahim Dauda