JPA - Devolución de una identificación generada automáticamente después de persist ()

113

Estoy usando JPA (EclipseLink) y Spring. Digamos que tengo una entidad simple con un ID generado automáticamente:

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

     // ...
}

En mi clase DAO, tengo un método de inserción que llama persist()a esta entidad. Quiero que el método devuelva el ID generado para la nueva entidad, pero cuando lo pruebo, devuelve en su 0lugar.

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

También tengo una clase de servicio que envuelve el DAO, si eso hace una diferencia:

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

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}
sura2k
fuente
Uno similar, puede consultar stackoverflow.com/q/3328813/366964
Nayan Wadekar
Gracias por las respuestas. Y como una solución complicada (no un JPA) podemos usar otra identificación única como la marca de tiempo de Unix.
sura2k
1
posible duplicado de ¿ Cuándo establece el JPA un @GeneratedValue @Id?
Raedwald

Respuestas:

184

Solo se garantiza que la identificación se generará en el momento de la descarga. La persistencia de una entidad solo la hace "adjunta" al contexto de persistencia. Entonces, descargue el administrador de la entidad explícitamente:

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

o devolver la propia entidad en lugar de su ID. Cuando finalice la transacción, se producirá la descarga, y los usuarios de la entidad fuera de la transacción verán el ID generado en la entidad.

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}
JB Nizet
fuente
10
NB: esto necesita anotar el campo de identificación con @GeneratedValue- lo que eso implique
Mr_and_Mrs_D
U puede por favor explicar los problemas al tratar de conseguir esto con composite Identificación stackoverflow.com/questions/31362100/...
bl3e
¿Existe una penalización en el rendimiento (o cualquier otro efecto negativo) de la descarga manual después de persistir?
Craig Otis
3
Sí, hay: ida y vuelta innecesarios a la base de datos si la transacción termina siendo revertida, posibles excepciones si la entidad persistente (u otras entidades vacías) aún no se encuentra en un estado válido. Un generador de secuencia o uuid es más simple y eficiente, y no tiene estos problemas porque la ID se genera y se asigna antes de que la entidad se escriba en la base de datos.
JB Nizet
1
@JBNizet, ¿necesita devolver la instancia o la referencia pasada sigue siendo válida? Quiero decir, ¿ insertABCcrea un nuevo objeto? ¿O modificar el anterior?
Ryvantage
13
@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;   
}

Verifique que la notación @GeneratedValue esté en su clase de entidad Esto le dice a JPA sobre el comportamiento generado automáticamente por la propiedad de su entidad

utkal patel
fuente
4

Así es como lo hice:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();
Koray Tugay
fuente
No funciona dando cero como Retorno después de conservar los datos en la tabla. la actualización no funciona en este caso. ¿Qué debo hacer para esto? por favor sugiera una manera ... Gracias
Vikrant Kashyap
1
@VikrantKashyap Por favor, publique una nueva pregunta con un código pequeño y mencióneme para que pueda echar un vistazo.
Koray Tugay
2

También puede usar GenerationType.TABLE en lugar de IDENTITY, que solo está disponible después de la inserción.

James
fuente
2
Solo una advertencia. Cuando probé GenerationType.TABLE, creó una tabla separada llamada hibernate_sequences y reinició la secuencia desde 1.
SriSri
0

Otra opción compatible con 4.0:

Antes de realizar los cambios, puede recuperar los nuevos CayenneDataObjectobjetos de la colección asociada al contexto, así:

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

luego acceda al ObjectIdpara cada uno en la colección, como:

ObjectId objectId = dataObject.getObjectId();

Finalmente, puede iterar bajo los valores, donde generalmente el ID generado será el primero de los valores (para una clave de columna única) en el Mapa devuelto por getIdSnapshot(), también contiene los nombres de columna asociados a la PK. como clave (s):

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

Así es como lo hice. Puedes probar

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

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}
Anh Khoa
fuente
-3
em.persist(abc);
em.refresh(abc);
return abc;
Andrey
fuente
Este método no funcionó para mí. Obtuve este error: javax.persistence.PersistenceException: org.hibernate.HibernateException: esta instancia aún no existe como una fila en la base de datos]
rtcarlson
@rtcarlson, sí, esto no funcionará. si está creando un nuevo objeto, lo que necesita em.flush()no es em.refresh(abc).
Ibrahim Dauda