2009-12-29 9 views
4

J'ai une configuration assez spéciale: je crée toutes les classes en Java, les connecte dans mon application (plusieurs relations ManyToOne). Puis, je voudrais parcourir mes objets et les enregistrer dans la base de données. Parfois, un objet est déjà dans la base de données, alors il ne devrait pas être encore persistant.EntityManager.merge insère des entités dupliquées

J'ai implémenté la méthode hashCode() et equals() -, mais mon em.merge() insère néanmoins les objets.

Encore une fois:

Je crée des objets, à savoir que je crée un certain joueur et mis dans quelle équipe ils sont. les équipes peuvent être des objets différents en Java, mais selon leur méthode "equals", ils sont identiques. Donc, si je sauvegarde un joueur, l'équipe devrait être enregistrée en conséquence (cela fonctionne), mais si l'équipe existe dans la base de données (selon la méthode equals), elle ne devrait pas être insérée à nouveau, mais la relation devrait être cours.

Qu'est-ce que je fais mal? Plus d'informations nécessaires?

private static void saveModels(final Set<?> models) { 
    EntityManagerFactory factory = null; 

    factory = Persistence.createEntityManagerFactory("sqlite"); 

    EntityManager manager = factory.createEntityManager(); 

    manager.getTransaction().begin(); 

    for (Object object : models) { 
     manager.merge(object); 
    } 

    manager.getTransaction().commit(); 

    manager.close(); 
    factory.close(); 
} 

modifier

@Entity 
public class Team { 

    private long id; 
    private String description; 

    @Id 
    @GeneratedValue 
    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    public String getDescription() { 
     return description; 
    } 

    public void setDescription(String description) { 
     this.description= description; 
    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see java.lang.Object#hashCode() 
    */ 
    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + description.length(); 
     return result; 
    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see java.lang.Object#equals(java.lang.Object) 
    */ 
    @Override 
    public boolean equals(final Object obj) { 
     if (this == obj) { 
      return true; 
     } 
     if (obj == null) { 
      return false; 
     } 
     if (getClass() != obj.getClass()) { 
      return false; 
     } 
     Team other = (Team) obj; 
     if (!description.equals(other.getDescription())) { 
      return false; 
     } 
     return true; 
    } 
} 


@Entity 
public class Player { 
    private long id; 
    private Team team; 
    private String name; 

    @Id 
    @GeneratedValue 
    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, targetEntity = Team.class) 
    @JoinColumn(name = "team_id") 
    public Team getTeam() { 
     return team; 
    } 

    public void setTeam(Team team) { 
     this.team = team; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 


    @Override 
    public int hashCode() { 
     return name.length(); 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (!(obj instanceof Player)) { 
      return false; 
     } 

     Player other = (Player) obj; 
     return other.getName().equals(name); 
    } 
} 
+0

fournissent le code de vos entités. – Bozho

Répondre

3

JPA utilise le champ @Id pour faire les fusions, il ne sera pas utiliser les méthodes equals et hashCode pour vérifier si une entité existe déjà dans la base de données. Ajoutez un mapping @OneToMany sur l'équipe, comme le suggère Bozho, même si je le ferais comme ça.

@OneToMany(cascade = { CascadeType.ALL }) 
private List<Player> players = new ArrayList<Player>(); 

public void addPlayer(Player player) { 
    player.setTeam(this); 
    players.add(player); 
} 

public Collection<Player> getPlayers() { 
    return new ArrayList<Player>(this.players); 
} 

Comme vous faites fusionner beaucoup de ENTITES à la fois, je suppose que vous faites une importation en vrac à partir d'un CSV ou quelque chose. Ensuite, plutôt que de créer une nouvelle Equipe/Joueur pour chaque ligne du CSV, gardez une Carte des Equipes avec le nom et ajoutez simplement les joueurs à l'équipe concernée.

Ainsi, au lieu de

Team t = new Team(); 
t.setName(teamName) 
Player p = new Player(); 
p.setName(playerName); 
p.setTeam(t); 

Do

Map<String, Team> teams = new HashMap<String,Team>(); 

... 

if (!teams.containsKey(teamName)) { 
    Team t = new Team(); 
    t.setDescription(teamName); 
    teams.put(teamName, t) 
} 

Player p = new Player(); 
p.setName(p); 
teams.get(teamName).addPlayer(p); 

... 

saveModels(teams.values()); 
+0

à mon avis pas l'approche la plus propre, mais il semble fonctionner, merci! – swalkner

0

Essayez de faire la relation bidirectionnelle par compte tenu de la classe équipe une collection de joueurs comme un champ. Ce serait annotées comme si

@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, targetEntity = Team.class) 
Collection<Player> getPlayers() { 
    return this.players; 
} 
+0

dois-je définir les joueurs dans la classe d'équipe dans mon application, aussi? parce que ce serait assez difficile, car je crée plusieurs classes d'équipe qui sont sémantiquement identiques ... j'espère que vous comprenez, ce que je veux dire ... – swalkner

+0

"plusieurs classes d'équipe" .. peut-être devriez-vous utiliser l'héritage là-bas? De toute façon, nous ne voyons pas encore l'image complète. – Bozho

+0

"plusieurs classes d'équipe" - ils sont tous de la même classe, mais différents objets, créés avec "new Team()"; mais comme ils contiennent tous la même description, ils ne devraient être sauvegardés qu'une seule fois dans la base de données. – swalkner

Questions connexes