EntityManager.merge 插入重复实体

发布于 2024-08-15 20:16:06 字数 3003 浏览 3 评论 0原文

我有一个非常特殊的设置:我用 Java 创建所有类,将它们连接到我的应用程序中(几个 ManyToOne 关系)。

然后,我想迭代我的对象并将它们保存到数据库中。有时,一个对象已经在数据库中,那么它不应该再次被持久化。

我正确实现了 hashCode() 和 equals() 方法,但我的 em.merge() 仍然插入了对象。

再说一遍:

我创建了一些对象,即我创建了一些玩家并设置了他们所在的球队。在 Java 中,团队可能是不同的对象,但根据它们的“等于”方法,它们是相同的。因此,如果我保存一个玩家,则应该相应地保存该团队(有效),但是如果该团队存在于数据库中(根据 equals 方法),则不应再次插入它,但应设置关系,课程。

我做错了什么?需要更多信息吗?

    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();
}

编辑

@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);
    }
}

I've got a pretty special setup: I create all the classes in Java, connect them in my application (several ManyToOne-relationships).

Then, I'd like to iterate over my objects and save them into the database. Sometimes, an object is already in the database, then it should not be persisted again.

I implemented the hashCode() and equals()-method correct, but my em.merge() inserts the objects nevertheless.

Again:

I create some objects, i.e. I create some player and set in which team they are. the teams may be different objects in Java, but according to their "equals"-method, they are the same. So if I save a player, the team should be saved accordingly (that works), but if the team exists in the database (according to the equals-method), it should not be inserted again, but the relationship should be set, of course.

What I'm I doing wrong? More information needed?

    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();
}

edit

@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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

虫児飞 2024-08-22 20:16:06

JPA 使用 @Id 字段进行合并,它不会使用 equals 和 hashCode 方法来检查数据库中是否已存在实体。

在团队上添加 @OneToMany 映射,就像 Bozho 建议的那样,尽管我会这样做。

@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);
}

当您同时合并大量实体时,我假设您正在从 CSV 或其他内容进行批量导入。然后,不要为 CSV 中的每一行创建一个新的团队/玩家,而是保留一个以名称为键的团队地图,然后将玩家添加到相关团队中。

所以,而不是

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

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());

JPA uses the @Id field to do the merges, it won't use the equals and hashCode methods to check if an entity already exists in the database.

Add a @OneToMany mapping on the Team, like Bozho suggests, although I'd do it like this.

@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);
}

As you're doing merging lots of entites at once, I'm assuming you're doing a bulk import from a CSV or something. Then, rather than creating a new Team/Player for every line in the CSV, keep a Map of Teams keyed by the name and just add the players to the relevant Team.

So, instead of

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());
向日葵 2024-08-22 20:16:06

尝试通过为 Team 类指定一个“玩家集合”作为字段来建立双向关系。这会像这样注释

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

Try making the relationship bi-directional by given the Team class a Collection of Players as a field. This would be annotated like so

@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, targetEntity = Team.class)
Collection<Player> getPlayers() {
  return this.players;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文