java - localcontainerentitymanagerfactorybean - Omitir GeneratedValue en Hibernate(fusionar datos no en db?)
spring jpa configuration (7)
Mi problema es el mismo que se describe en [1] o [2] . Necesito configurar manualmente un valor autogenerado por defecto ( ¿por qué? Importar datos antiguos ). Como se describe en [1] usando la entity = em.merge(entity)
de Hibernate entity = em.merge(entity)
hará el truco.
Desafortunadamente para mí no es así. No recibo un error ni ninguna otra advertencia. La entidad simplemente no va a aparecer en la base de datos . Estoy usando Spring and Hibernate EntityManager 3.5.3-Final.
¿Algunas ideas?
Actualizando la respuesta de Laurent Grégoire para hibernate 5.2 porque parece haber cambiado un poco.
public class UseExistingIdOtherwiseGenerateUsingIdentity extends IdentityGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
y utilícelo así: (reemplace el nombre del paquete)
@Id
@GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "{package}.UseExistingIdOtherwiseGenerateUsingIdentity")
@GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
@Column(unique = true, nullable = false)
protected Integer id;
De acuerdo con la generación de inhabilitación selectiva de una nueva cadena de ID en los foros de Hibernate, merge()
podría no ser la solución (al menos no solo) y es posible que deba usar un generador personalizado (ese es el segundo enlace que publicó).
No lo probé yo mismo, así que no puedo confirmarlo, pero recomiendo leer el hilo de los foros de Hibernate.
Estoy dando una solución aquí que funcionó para mí:
crea tu propio generador de identificador / generador de secuencia
public class FilterIdentifierGenerator extends IdentityGenerator implements IdentifierGenerator{
@Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
// TODO Auto-generated method stub
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
modifique su entidad como:
@Id
@GeneratedValue(generator="myGenerator")
@GenericGenerator(name="myGenerator", strategy="package.FilterIdentifierGenerator")
@Column(unique=true, nullable=false)
private int id;
...
y al guardar en lugar de usar persist()
use merge()
o update()
Necesitas una transacción en ejecución.
En caso de que su transacción se administre manualmente:
entityManager.getTransaction().begin();
(por supuesto no te olvides de cometer)
Si está utilizando transacciones declarativas, use la declaración apropiada (a través de anotaciones, lo más probable)
Además, establezca el nivel de registro de hibernación para debug
( log4j.logger.org.hibernate=debug
) en su log4j.properties para rastrear lo que está sucediendo en más detalles.
Otra implementación, mucho más simple.
Éste funciona con configuración basada en anotaciones o basada en xml: se basa en los metadatos de hibernación para obtener el valor de identificación para el objeto. Reemplace SequenceGenerator
por IdentityGenerator
(o cualquier otro generador) dependiendo de su configuración. (La creación de un decorador en lugar de subclases, pasando el generador de ID decorado como un parámetro para este generador, se deja como un ejercicio para el lector).
public class UseExistingOrGenerateIdGenerator extends SequenceGenerator {
@Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
Responda al ejercicio (usando un patrón de decorador, como se solicita), no realmente probado:
public class UseExistingOrGenerateIdGenerator implements IdentifierGenerator, Configurable {
private IdentifierGenerator defaultGenerator;
@Override
public void configure(Type type, Properties params, Dialect d)
throws MappingException;
// For example: take a class name and create an instance
this.defaultGenerator = buildGeneratorFromParams(
params.getProperty("default"));
}
@Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : defaultGenerator.generate(session, object);
}
}
Para cualquier otra persona que busque hacer esto, arriba funciona bien. Solo una recomendación para obtener el identificador del objeto en lugar de tener herencia para cada clase de Entidad (Solo para el ID), podría hacer algo como:
import org.hibernate.id.IdentityGenerator;
public class UseIdOrGenerate extends IdentityGenerator {
private static final Logger log = Logger.getLogger(UseIdOrGenerate.class
.getName());
@Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
if (object == null)
throw new HibernateException(new NullPointerException());
for (Field field : object.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(Id.class)
&& field.isAnnotationPresent(GeneratedValue.class)) {
boolean isAccessible = field.isAccessible();
try {
field.setAccessible(true);
Object obj = field.get(object);
field.setAccessible(isAccessible);
if (obj != null) {
if (Integer.class.isAssignableFrom(obj.getClass())) {
if (((Integer) obj) > 0) {
return (Serializable) obj;
}
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return super.generate(session, object);
}
}
funciona en mi proyecto con el siguiente código:
@XmlAttribute
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY, generator="IdOrGenerated")
@GenericGenerator(name="IdOrGenerated",
strategy="....UseIdOrGenerate"
)
@Column(name = "ID", nullable = false)
private Integer id;
y
import org.hibernate.id.IdentityGenerator;
...
public class UseIdOrGenerate extends IdentityGenerator {
private static final Logger log = Logger.getLogger(UseIdOrGenerate.class.getName());
@Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
if (obj == null) throw new HibernateException(new NullPointerException()) ;
if ((((EntityWithId) obj).getId()) == null) {
Serializable id = super.generate(session, obj) ;
return id;
} else {
return ((EntityWithId) obj).getId();
}
}
donde básicamente defines tu propio generador de ID (basado en la estrategia de Identidad), y si el ID no está establecido, delegas la generación al generador predeterminado.
El principal inconveniente es que lo conecta con Hibernate como proveedor de JPA ... pero funciona perfectamente con mi proyecto MySQL