我有2个班:司机和汽车。汽车表在单独的过程中更新。我需要的是在驱动程序中拥有属性,允许我读取完整的汽车描述并只写入指向现有汽车的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对象只是典型的JPA对象,没有对驱动程序的反向引用。
所以我试图通过这个来实现: 1)我可以使用详细的JSON视图阅读完整的汽车描述 2)或者我只能在简短的JsonView中读取汽车的Id 3)最重要的是,在创建新的驱动程序时,我只想传递汽车的JSON ID。 这样我就不需要在持久化期间为汽车执行不必要的读取操作,而只是更新Id。
我得到以下错误:"对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例:com.Driver.car - > com.Car"
我不想在DB中更新Car的实例,而只是从Driver引用它。知道怎么做到我想要的吗?
谢谢。
更新: 忘记提及我在创建驱动程序期间传递的汽车ID是DB中现有汽车的有效ID。
答案 0 :(得分:13)
您可以通过getReference
中的EntityManager
来电:
EntityManager em = ...;
Car car = em.getReference(Car.class, carId);
Driver driver = ...;
driver.setCar(car);
em.persist(driver);
这不会从数据库执行SELECT语句。
答案 1 :(得分:7)
作为okutane的答案,请参阅片段:
@JoinColumn(name = "car_id", insertable = false, updatable = false)
@ManyToOne(targetEntity = Car, fetch = FetchType.EAGER)
private Car car;
@Column(name = "car_id")
private Long carId;
所以这里发生的是当你想进行插入/更新时,你只填充carId字段并执行插入/更新。由于car字段是不可插入的且不可更新的,Hibernate不会抱怨这一点,因为在你的数据库模型中你只能将你的car_id填充为外键,无论如何这已经足够了(你的数据库上的外键关系将会确保您的数据完整性)。现在,当您获取实体时,Hibernate将填充car字段,为您提供灵活性,只需在需要时获取父项。
答案 2 :(得分:5)
您只能使用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;
答案 3 :(得分:4)
该错误消息表示您的对象图中有一个未明确保留的瞬态实例。简要回顾一下对象在JPA中可以拥有的状态:
错误消息告诉您的是,您正在使用的(托管/已分离)Driver对象持有对Hibernate未知的Car对象的引用(它是瞬态的)。为了使Hibernate能够理解保存的任何未保存的Car实例也应该保存,你可以调用EntityManager的persist-method。
或者,你可以在persist上添加一个级联(我想,只是从头顶开始,我们还没有对它进行过测试),这会在持久化Driver之前在Car上执行一个持久化。
@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
@JoinColumn(name = "CAR_ID")
private Car car;
如果您使用entitymanager的merge-method存储驱动程序,则应添加CascadeType.MERGE
,或两者都添加:
@ManyToOne(fetch=FetchType.LAZY, cascade={ CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name = "CAR_ID")
private Car car;
答案 4 :(得分:3)
public void setCarId(long carId) {
this.car = new Car (carId);
}
它实际上不是car
的保存版本。所以它是瞬态对象,因为它没有id
。 JPA要求你应该关心关系。如果实体是新的(不由上下文管理),则应该在它与其他托管/分离对象相关之前保存它(实际上MASTER实体可以通过使用级联来维护它的子代)。
两种方式:级联或从db中保存和检索。
此外,您应该避免手动设置实体ID
。 如果您不想通过它的MASTER实体更新/保留汽车,您应该从数据库获取CAR
并使用<维护驱动程序 strong>它的实例。因此,如果你这样做,Car
将与持久化上下文分离,但仍然会有ID和ID,并且可以与没有影响的任何实体相关联。
答案 5 :(得分:0)
添加如下所示等于false的可选字段
@ManyToOne(optional = false) // Telling hibernate trust me (As the creator of 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));
然后保持驱动程序正常
driverRepo.save(driver);
您将看到ID为1的汽车已完美分配给数据库中的驾驶员
说明:
那么使optional=false
如此微小的原因可能会帮助更多https://stackoverflow.com/a/17987718
答案 6 :(得分:-2)
在manytoone注释中使用级联 @ManyToOne(级联= CascadeType.Remove)