EntityManager.merge вкладывает дублируемые сущности
-
21-09-2019 - |
Вопрос
У меня есть довольно специальная настройка: я создаю все классы в Java, подключаю их в своем приложении (несколько многотоновых взаимосвязи).
Затем я хотел бы итерации над своими объектами и сохранить их в базе данных. Иногда объект уже находится в базе данных, тогда его не следует сохранять снова.
Я реализовал HashCode () и Equals ()-Method правильно, но, тем не менее, мой em.merge () вводит объекты.
Опять таки:
Я создаю несколько объектов, то есть я создаю некоторых игроков и установил, в какой они команда. Команды могут быть разными объектами в Java, но в соответствии с их «равными»-методом они одинаковы. Поэтому, если я спасу игрока, команда должна быть сохранена соответственно (которая работает), но если команда существует в базе данных (в соответствии с Method 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);
}
}
Решение
JPA использует поле @ID для выполнения слияний, оно не будет использовать методы равных и хэшкодов, чтобы проверить, существует ли в базе данных объект.
Добавьте сопоставление @onetomany в команде, как предполагает Божо, хотя я бы сделал это так.
@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());
Другие советы
Попробуйте сделать отношения двунаправленными отношениями, предоставив коллекцию игроков класса Team A в качестве поля. Это будет аннотировано так
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, targetEntity = Team.class)
Collection<Player> getPlayers() {
return this.players;
}