Mon problème est le même que celui décrit dans [1] ou [2] . Je dois définir manuellement une valeur générée automatiquement par défaut ( pourquoi? Importer d'anciennes données). Comme décrit dans [1] en utilisant la fonction entity = em.merge(entity)
d'Hibernate fera l'affaire.
Malheureusement pour moi, ce n'est pas le cas. Je ne reçois ni erreur ni autre avertissement. L'entité est juste ne va pas apparaître dans la base de données . J'utilise Spring et Hibernate EntityManager 3.5.3-Final.
Des idées?
cela fonctionne sur mon projet avec le code suivant:
@XmlAttribute
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY, generator="IdOrGenerated")
@GenericGenerator(name="IdOrGenerated",
strategy="....UseIdOrGenerate"
)
@Column(name = "ID", nullable = false)
private Integer id;
et
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();
}
}
où vous définissez essentiellement votre propre générateur d'ID (basé sur la stratégie d'identité), et si l'ID n'est pas défini, vous déléguez la génération au générateur par défaut.
Le principal inconvénient est qu'il vous limite à Hibernate en tant que fournisseur JPA ... mais il fonctionne parfaitement avec mon projet MySQL
Une autre implémentation, beaucoup plus simple.
Celui-ci fonctionne avec les deux configuration basée sur des annotations ou basée sur xml: il s'appuie sur des métadonnées d'hibernation pour obtenir la valeur id de l'objet. Remplacez SequenceGenerator
par IdentityGenerator
(ou tout autre générateur) selon votre configuration. (La création d'un décorateur au lieu d'un sous-classement, en passant le générateur d'ID décoré en paramètre à ce générateur, est laissée au lecteur comme exercice).
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);
}
}
Réponse à l'exercice (en utilisant un motif de décoration, comme demandé), pas vraiment testé:
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);
}
}
Mise à jour de la réponse de Laurent Grégoire pour l'hibernate 5.2 car elle semble avoir un peu changé.
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);
}
}
et utilisez-le comme ceci: (remplacez le nom du paquet)
@Id
@GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "{package}.UseExistingIdOtherwiseGenerateUsingIdentity")
@GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
@Column(unique = true, nullable = false)
protected Integer id;
Si vous utilisez le org.hibernate.id.UUIDGenerator
D'hibernate pour générer un identifiant de chaîne, je vous suggère d'utiliser:
public class UseIdOrGenerate extends UUIDGenerator {
@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);
}
}
Je donne ici une solution qui a fonctionné pour moi:
créez votre propre générateur d'identifiant/générateur de séquence
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);
}
}
modifier votre entité comme:
@Id
@GeneratedValue(generator="myGenerator")
@GenericGenerator(name="myGenerator", strategy="package.FilterIdentifierGenerator")
@Column(unique=true, nullable=false)
private int id;
...
et lors de l'enregistrement au lieu d'utiliser persist()
utilisez merge()
ou update()
Selon le thread Désactiver sélectivement la génération d'un nouvel ID sur les forums Hibernate, merge()
n'est peut-être pas la solution (du moins pas seul) et vous devrez peut-être utiliser un - générateur personnalisé (c'est le deuxième lien que vous avez publié).
Je n'ai pas testé cela moi-même donc je ne peux pas le confirmer mais je recommande de lire le fil des forums d'Hibernate.
Pour tous ceux qui cherchent à faire cela, ce qui précède fonctionne bien. Juste une recommandation pour obtenir l'identifiant de l'objet plutôt que d'avoir l'héritage pour chaque classe d'entité (juste pour l'ID), vous pouvez faire quelque chose comme:
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);
}
}
Vous avez besoin d'une transaction en cours.
Si votre transaction est gérée manuellement:
entityManager.getTransaction().begin();
(bien sûr, n'oubliez pas de vous engager)
Si vous utilisez des transactions déclaratives, utilisez la déclaration appropriée (via des annotations, très probablement)
Définissez également le niveau de journalisation de la mise en veille prolongée sur debug
(log4j.logger.org.hibernate=debug
) dans votre log4j.properties afin de suivre ce qui se passe plus en détail.