Trouver ou insert basé sur clé unique avec Hibernate

Je suis en train d'écrire une méthode qui renvoie une Hibernate objet basé sur un unique, mais non de la clé primaire. Si l'entité qui existe déjà dans la base de données que je veux renvoyer, mais si ce n'est pas que je veux créer une nouvelle instance et de l'enregistrer avant de le retourner.

Mise à JOUR: Permettez-moi de préciser que l'application que je suis en train d'écrire ce est fondamentalement un traitement par lot des fichiers d'entrée. Le système a besoin de lire un fichier ligne par ligne et insérer des enregistrements dans la db. Le format de fichier est en fait un dénormalisée vue de plusieurs tables dans notre schéma de ce que j'ai à faire est d'analyser l'enregistrement parent soit l'insérer dans la db de sorte que je peux obtenir une nouvelle synthèse de la clé, ou si il existe déjà le sélectionner. Ensuite, je peux ajouter d'autres enregistrements associés dans d'autres tables qui ont des clés étrangères de retour pour cet enregistrement.

La raison pour laquelle ce qui est difficile, c'est que chaque fichier doit être totalement ou non importé importé du tout, c'est à dire toutes les insertions et mises à jour effectuées pour un fichier donné devrait être une partie d'une transaction. C'est assez facile si il n'y a qu'un seul processus qui fait toutes les importations, mais j'aimerais briser ce sur plusieurs serveurs, si possible. En raison de ces contraintes, j'ai besoin d'être en mesure de rester à l'intérieur d'une transaction, mais gérer les exceptions où un dossier existe déjà.

Mappés classe pour les enregistrements parent ressemble à ceci:

@Entity
public class Foo {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;
    @Column(unique = true)
    private String name;
    ...
}

Ma première tentative à la rédaction de cette méthode est comme suit:

public Foo findOrCreate(String name) {
    Foo foo = new Foo();
    foo.setName(name);
    try {
        session.save(foo)
    } catch(ConstraintViolationException e) {
        foo = session.createCriteria(Foo.class).add(eq("name", name)).uniqueResult();
    }
    return foo;
}

Le problème, c'est quand le nom, je suis à la recherche existe, un org.mise en veille prolongée.AssertionFailure exception est levée par l'appel à uniqueResult(). Le plein de trace de pile est ci-dessous:

org.hibernate.AssertionFailure: null id in com.searchdex.linktracer.domain.LinkingPage entry (don't flush the Session after an exception occurs)
at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:82) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:190) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:147) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1185) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1709) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]
at org.hibernate.impl.CriteriaImpl.uniqueResult(CriteriaImpl.java:369) [hibernate-core-3.6.0.Final.jar:3.6.0.Final]

Personne ne sait quelle est la cause de cette exception? N'mise en veille prolongée une meilleure façon d'accomplir cette?

Permettez-moi également de manière préventive expliquer pourquoi je suis en insérant d'abord, puis en sélectionnant si et quand cela échoue. Ce besoin de travailler dans un environnement distribué, donc je ne peux pas synchroniser à travers le vérifier pour voir si l'enregistrement existe déjà et de l'insertion. La meilleure façon de le faire est de laisser la base de données gérer cette synchronisation par la vérification de la violation de la contrainte sur chaque insertion.

Qu'entendez-vous par "environnement distribué"? Utilisez-vous une grille de base des SGBDR?
par distribués, je veux dire que j'ai plusieurs clients sur les différentes machines de l'exécution de ce code contre une seule base de données centralisée. Deux clients différents pourrait tenter d'insérer le même enregistrement dans le même temps, dans ce cas, le "premier" on doit gagner et être conservées et l'autre client doit juste retour de ce qui a déjà été conservées.
n'êtes-vous pas vraiment de voir les conséquences de ne pas briser les données correctement?
Je ne suis pas sûr de ce que vous entendez par "casser les données correctement."
Avez-vous envisager de pré-traitement d'un lot de données de sorte qu'il n'y a pas de conflits? Ou tout simplement de traiter le conflit parties d'abord, dans un seul thread?

OriginalL'auteur Mike Deck | 2011-02-16