java - query - Spring-Data JPA: guarda una nueva entidad haciendo referencia a una existente
spring data native sql (3)
Lo mejor que se me ocurrió es
public final T save(T containable) {
// if entity containable.getCompound already exists, it
// must first be reattached to the entity manager or else
// an exception will occur (issue in Spring Data JPA ->
// save() method internal calls persists instead of merge)
if (containable.getId() == null
&& containable.getCompound().getId() != null){
Compound compound = getCompoundService()
.getById(containable.getCompound().getId());
containable.setCompound(compound);
}
containable = getRepository().save(containable);
return containable;
}
Verificamos si nos encontramos en una situación problemática y, en caso afirmativo, simplemente volvemos a cargar la entidad existente desde la base de datos con su ID y configuramos el campo de la nueva entidad en esta instancia recién cargada. Luego se adjuntará.
Esto requiere que el servicio para la nueva entidad contenga una referencia al servicio de la entidad referenciada. Esto no debería ser un problema, ya que de todos modos está utilizando Spring para que el servicio se pueda agregar como un nuevo campo @Autowired
.
Sin embargo, otro problema (en mi caso, este comportamiento es realmente deseado) es que no puede cambiar la entidad existente a la que se hace referencia al mismo tiempo mientras guarda la nueva. Todos esos cambios serán ignorados.
NOTA IMPORTANTE:
En muchos y probablemente sus casos esto puede ser mucho más simple. Puede agregar una referencia de administrador de entidades a su servicio:
@PersistenceContext
private EntityManager entityManager;
y en lo anterior if(){}
uso bloque
containable = entityManager.merge(containable);
en lugar de mi código (no probado si funciona).
En mi caso, las clases son abstractas y targetEntity
en @ManyToOne
es, por lo tanto, también abstracta. Llamar a entityManager.merge (contenible) directamente luego conduce a una excepción. Sin embargo, si sus clases son todas concretas esto debería funcionar.
La pregunta es básicamente la misma que la de abajo:
La persistencia en cascada JPA y las referencias a entidades separadas arrojan PersistentObjectException. ¿Por qué?
Estoy creando una nueva entidad que hace referencia a una existente, separada. Ahora, cuando guardo esta entidad en mi repositorio de datos de primavera, se lanza una excepción:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist
Si observamos el método save () en el código fuente de JPA de datos de primavera, vemos:
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
y si miramos isNew () en AbstractEntityInformation
public boolean isNew(T entity) {
return getId(entity) == null;
}
Entonces, básicamente, si guardo () una nueva entidad (id == null)
, los datos de primavera siempre llamarán persistir y, por lo tanto, este escenario siempre fallará.
Este parece ser un caso de uso muy típico cuando se agregan nuevos elementos a las colecciones.
¿Cómo puedo resolver esto?
EDITAR 1:
NOTA:
Este problema NO está directamente relacionado con ¿Cómo guardar una nueva entidad que haga referencia a una entidad existente en Spring JPA? . Para elaborar, suponga que obtiene la solicitud para crear la nueva entidad a través de http. A continuación, extraiga la información de la solicitud y cree su entidad y la referenciada existente. Por lo tanto, siempre serán desapegados.
Tengo el mismo problema con un @EmbeddedId
y datos de negocios como parte de la identificación.
La única forma de saber si la entidad es nueva es realizar una (em.find(entity)==null)?em.persist(entity):em.merge(entity)
pero los datos de primavera solo proporcionan el método save()
y no hay manera de llenar el método Persistable.isNew()
con un método find()
.
Tuve un problema similar cuando intentaba guardar un nuevo objeto de entidad con un objeto de entidad ya guardado dentro.
Lo que hice fue implementado Persistable <T> e implementado esNew () en consecuencia.
public class MyEntity implements Persistable<Long> {
public boolean isNew() {
return null == getId() &&
subEntity.getId() == null;
}
O puede usar AbstractPersistable y anular el isNew allí por supuesto.
No sé si esto se considerará una buena manera de manejar este problema, pero funcionó bastante bien para mí y además me parece muy natural hacerlo.