我目前正在编写一个相当“愚蠢”的ETL过程。我称之为dumb,是因为这个进程每晚运行,消化所有数据(即使数据没有变化),并将其持久化。我的希望是保持简单。
我一直在使用Hibernate v6.1.7来定义我的实体并持久化它们。由于实体可能已经存在于我的数据库中,所以我使用合并来持久化实体。
public static void main(String[] args){
BaseballGame game = new BaseballGame();
BaseballWeather weather = new BaseballWeather();
game.setGameId(12345L);
game.setWeather(weather);
weather.setTemperature(70.0);
weather.setGame(game);
Session session = Hibernate.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.merge(game);
transaction.commit();
session.close();
}
这对于没有任何复杂关系的简单实体和具有ManyToOne关系的实体都很有效。
我在持久化一个具有一对一关系的实体时遇到了问题,其中子实体拥有密钥。下面是一些展示此问题的实体的简单示例。
@Entity
@Table(name = "baseball_game")
public class BaseballGame {
@Id
private Long gameId;
@OneToOne(mappedBy = "game", cascade = CascadeType.MERGE)
private BaseballWeather weather;
public void setGameId(Long gameId) {
this.gameId = gameId;
}
public void setWeather(BaseballWeather weather) {
this.weather = weather;
}
}
@Entity
@Table(name = "baseball_weather")
public class BaseballWeather {
@Id
private Long id;
private Double temperature;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "game_id")
@MapsId
private BaseballGame game;
public void setTemperature(Double temperature) {
this.temperature = temperature;
}
public void setGame(BaseballGame game) {
this.game = game;
}
}
如果我从上面运行主代码,它将在第一次运行时成功。但是一旦baseball_weather存在,运行merge会产生以下错误。
线程“main”中出现异常jakarta.persistence.EntityExistsException:具有相同标识符值的其他对象已与会话关联:[x.y.z.BaseballWeather#12345]
错误消息是正确的,我正在手动创建一个版本的BaseballWeather类,它包含与会话中已存储的对象相同的ID。但是我没有看到父类或其他没有这种一对一关系的实体的这种行为。在这些情况下,我手动创建的对象仍然会被合并而不会出错。
我的印象是,将级联类型指定为“Merge”将级联合并操作。我试过替换其他级联类型(PersistAll等),如果对象不存在,没有一个能够持久化对象,如果对象不存在,也没有一个能够更新对象。
有谁知道我如何更新我的实体来支持持久化和通过merge方法更新而不会出错吗?
1条答案
按热度按时间uujelgoq1#
好的,我明白了。下面是重现错误所需的完整代码:
所以这里的问题是由我添加的注解行引起的。Hibernate推断分离的
BaseballWeather
是一个新的/临时的对象(在数据库中不存在),因为它有一个空的@Id
属性。这是一个我实际上没有意识到会发生的情况。只有当您与
@MapsId
有这些一对一的关联时才会发生这种情况。无论如何,我将进一步研究这个问题,看看我们是否可以在这里报告一个更好的错误,但要明确的是,这实际上是你忘记设置
id
字段的错误。