JPA many-to-one 关系 - 只需要保存 Id
JPA many-to-one relation - need to save only Id
我有 2 类: Driver 和汽车。汽车 table 在单独的过程中更新。我需要的是 Driver 中的 属性 允许我阅读完整的汽车描述并只写指向现有汽车的 Id。这是示例:
@Entity(name = "DRIVER")
public class Driver {
... ID and other properties for Driver goes here .....
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "CAR_ID")
private Car car;
@JsonView({Views.Full.class})
public Car getCar() {
return car;
}
@JsonView({Views.Short.class})
public long getCarId() {
return car.getId();
}
public void setCarId(long carId) {
this.car = new Car (carId);
}
}
Car object 只是典型的 JPA object,没有对 Driver.
的反向引用
所以我试图通过此实现的是:
- 我可以使用详细的 JSON 视图
阅读完整的汽车描述
- 或者我只能在 Short JsonView 中读取汽车的 Id
- 最重要的是,在创建新的 Driver 时,我只想传入 JSON 汽车的 ID。
这样我就不需要在持续期间对汽车进行不必要的读取,而只需更新 Id。
我收到以下错误:
object references an unsaved transient instance - save the transient instance before flushing : com.Driver.car -> com.Car
我不想在数据库中更新 Car 的实例,而只是从 Driver 中引用它。知道如何实现我想要的吗?
谢谢。
更新:
忘记提及我在创建 Driver 期间传递的汽车 ID 是数据库中现有汽车的有效 ID。
在manytoone注解中使用级联
@manytoone(级联=CascadeType.Remove)
public void setCarId(long carId) {
this.car = new Car (carId);
}
实际上是一个car
版本没有保存。所以它是一个瞬态对象,因为它没有id
。 JPA 要求您注意关系。如果实体是新的(不受上下文管理),则应先保存它,然后才能与其他 managed/detached 对象关联(实际上,MASTER 实体可以通过使用级联来维护它的子实体)。
两种方式:级联或从数据库中保存和检索。
您还应该避免手动设置实体 ID
。 如果您不想 update/persist 汽车 通过它的 MASTER 实体,您应该从数据库中获取 CAR
并维护您的 driver 与 它是实例 。因此,如果您这样做,Car
将从持久性上下文中分离出来,但它仍然具有 ID 并且可以与任何实体关联而不会产生影响。
该错误消息意味着您的对象图中有一个未明确保留的临时实例。对象在 JPA 中可以具有的状态的简短回顾:
- Transient: 尚未存储在数据库中的新对象(因此对于 entitymanager 而言是未知的。)没有设置 id。
- Managed:entitymanager 跟踪的对象。托管对象是您在事务范围内使用的对象,一旦提交事务,对托管对象所做的所有更改将自动存储。
- Detached:事务提交后仍可访问的以前管理的对象。 (事务外的托管对象。)设置了 id。
错误消息告诉您的是,您正在使用的 (managed/detached) Driver-object 持有对 Hibernate 未知的 Car-object 的引用(它是瞬态的)。为了让 Hibernate 理解任何未保存的从 Driver 引用的 Car 实例也应该被保存,你可以调用 EntityManager 的持久化方法。
或者,您可以在 persist 上添加一个级联(我认为,只是从我的头顶开始,还没有测试过),这将在持久化 Driver 之前在 Car 上执行 persist。
@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
@JoinColumn(name = "CAR_ID")
private Car car;
如果您使用实体管理器的合并方法来存储驱动程序,您应该添加 CascadeType.MERGE
代替,或两者:
@ManyToOne(fetch=FetchType.LAZY, cascade={ CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name = "CAR_ID")
private Car car;
您可以通过 getReference
调用 EntityManager
:
EntityManager em = ...;
Car car = em.getReference(Car.class, carId);
Driver driver = ...;
driver.setCar(car);
em.persist(driver);
这不会从数据库执行 SELECT 语句。
您只能像这样使用 car
ID:
@JoinColumn(name = "car")
@ManyToOne(targetEntity = Car.class, fetch = FetchType.LAZY)
@NotNull(message = "Car not set")
@JsonIgnore
private Car car;
@Column(name = "car", insertable = false, updatable = false)
private Long carId;
作为对 okutane 的回答,请参阅片段:
@JoinColumn(name = "car_id", insertable = false, updatable = false)
@ManyToOne(targetEntity = Car.class, fetch = FetchType.EAGER)
private Car car;
@Column(name = "car_id")
private Long carId;
所以这里发生的是,当您想要执行 insert/update 时,您只需填充 carId
字段并执行 insert/update。由于 car 字段是 non-insertable 和 non-updatable Hibernate 不会抱怨这个,因为在你的数据库模型中你只会填充你的 car_id
作为外键无论如何这在这一点上就足够了(并且您在数据库上的外键关系将确保您的数据完整性)。现在,当您获取您的实体时,汽车字段将由 Hibernate 填充,从而为您提供灵活性,在需要时只获取您的 parent。
添加可选字段等于 false,如下所示
@ManyToOne(optional = false) // Telling hibernate trust me (As a trusted developer in this project) when building the query that the id provided to this entity is exists in database thus build the insert/update query right away without pre-checks
private Car car;
这样你就可以将汽车的 ID 设置为
driver.setCar(new Car(1));
然后坚持driver正常
driverRepo.save(driver);
你会看到 id 为 1 的汽车在数据库
中完美地分配给了 driver
描述:
所以这个小小的 optional=false
可能会有所帮助 https://whosebug.com/a/17987718
我有 2 类: Driver 和汽车。汽车 table 在单独的过程中更新。我需要的是 Driver 中的 属性 允许我阅读完整的汽车描述并只写指向现有汽车的 Id。这是示例:
@Entity(name = "DRIVER")
public class Driver {
... ID and other properties for Driver goes here .....
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "CAR_ID")
private Car car;
@JsonView({Views.Full.class})
public Car getCar() {
return car;
}
@JsonView({Views.Short.class})
public long getCarId() {
return car.getId();
}
public void setCarId(long carId) {
this.car = new Car (carId);
}
}
Car object 只是典型的 JPA object,没有对 Driver.
的反向引用所以我试图通过此实现的是:
- 我可以使用详细的 JSON 视图 阅读完整的汽车描述
- 或者我只能在 Short JsonView 中读取汽车的 Id
- 最重要的是,在创建新的 Driver 时,我只想传入 JSON 汽车的 ID。 这样我就不需要在持续期间对汽车进行不必要的读取,而只需更新 Id。
我收到以下错误:
object references an unsaved transient instance - save the transient instance before flushing : com.Driver.car -> com.Car
我不想在数据库中更新 Car 的实例,而只是从 Driver 中引用它。知道如何实现我想要的吗?
谢谢。
更新: 忘记提及我在创建 Driver 期间传递的汽车 ID 是数据库中现有汽车的有效 ID。
在manytoone注解中使用级联 @manytoone(级联=CascadeType.Remove)
public void setCarId(long carId) {
this.car = new Car (carId);
}
实际上是一个car
版本没有保存。所以它是一个瞬态对象,因为它没有id
。 JPA 要求您注意关系。如果实体是新的(不受上下文管理),则应先保存它,然后才能与其他 managed/detached 对象关联(实际上,MASTER 实体可以通过使用级联来维护它的子实体)。
两种方式:级联或从数据库中保存和检索。
您还应该避免手动设置实体 ID
。 如果您不想 update/persist 汽车 通过它的 MASTER 实体,您应该从数据库中获取 CAR
并维护您的 driver 与 它是实例 。因此,如果您这样做,Car
将从持久性上下文中分离出来,但它仍然具有 ID 并且可以与任何实体关联而不会产生影响。
该错误消息意味着您的对象图中有一个未明确保留的临时实例。对象在 JPA 中可以具有的状态的简短回顾:
- Transient: 尚未存储在数据库中的新对象(因此对于 entitymanager 而言是未知的。)没有设置 id。
- Managed:entitymanager 跟踪的对象。托管对象是您在事务范围内使用的对象,一旦提交事务,对托管对象所做的所有更改将自动存储。
- Detached:事务提交后仍可访问的以前管理的对象。 (事务外的托管对象。)设置了 id。
错误消息告诉您的是,您正在使用的 (managed/detached) Driver-object 持有对 Hibernate 未知的 Car-object 的引用(它是瞬态的)。为了让 Hibernate 理解任何未保存的从 Driver 引用的 Car 实例也应该被保存,你可以调用 EntityManager 的持久化方法。
或者,您可以在 persist 上添加一个级联(我认为,只是从我的头顶开始,还没有测试过),这将在持久化 Driver 之前在 Car 上执行 persist。
@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
@JoinColumn(name = "CAR_ID")
private Car car;
如果您使用实体管理器的合并方法来存储驱动程序,您应该添加 CascadeType.MERGE
代替,或两者:
@ManyToOne(fetch=FetchType.LAZY, cascade={ CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name = "CAR_ID")
private Car car;
您可以通过 getReference
调用 EntityManager
:
EntityManager em = ...;
Car car = em.getReference(Car.class, carId);
Driver driver = ...;
driver.setCar(car);
em.persist(driver);
这不会从数据库执行 SELECT 语句。
您只能像这样使用 car
ID:
@JoinColumn(name = "car")
@ManyToOne(targetEntity = Car.class, fetch = FetchType.LAZY)
@NotNull(message = "Car not set")
@JsonIgnore
private Car car;
@Column(name = "car", insertable = false, updatable = false)
private Long carId;
作为对 okutane 的回答,请参阅片段:
@JoinColumn(name = "car_id", insertable = false, updatable = false)
@ManyToOne(targetEntity = Car.class, fetch = FetchType.EAGER)
private Car car;
@Column(name = "car_id")
private Long carId;
所以这里发生的是,当您想要执行 insert/update 时,您只需填充 carId
字段并执行 insert/update。由于 car 字段是 non-insertable 和 non-updatable Hibernate 不会抱怨这个,因为在你的数据库模型中你只会填充你的 car_id
作为外键无论如何这在这一点上就足够了(并且您在数据库上的外键关系将确保您的数据完整性)。现在,当您获取您的实体时,汽车字段将由 Hibernate 填充,从而为您提供灵活性,在需要时只获取您的 parent。
添加可选字段等于 false,如下所示
@ManyToOne(optional = false) // Telling hibernate trust me (As a trusted developer in this project) when building the query that the id provided to this entity is exists in database thus build the insert/update query right away without pre-checks
private Car car;
这样你就可以将汽车的 ID 设置为
driver.setCar(new Car(1));
然后坚持driver正常
driverRepo.save(driver);
你会看到 id 为 1 的汽车在数据库
中完美地分配给了 driver描述:
所以这个小小的 optional=false
可能会有所帮助 https://whosebug.com/a/17987718