Java JPA 多对一关系 - 只需要保存 Id
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27930449/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JPA many-to-one relation - need to save only Id
提问by Andrei V
I have 2 classes: Driver and Car. Cars table updated in separate process. What I need is to have property in Driver that allows me to read full car description and write only Id pointing to existing Car. Here is example:
我有 2 个类:Driver 和 Car。汽车表在单独的过程中更新。我需要的是 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 is just typical JPA object with no back reference to the Driver.
Car 对象只是典型的 JPA 对象,没有对 Driver 的反向引用。
So what I was trying to achieve by this is: 1) I can read full Car description using detailed JSON View 2) or I can read only Id of the Car in Short JsonView 3) and most important, when creating new Driver I just want to pass in JSON ID of the car. This way I dont need to do unnesessery reads for the Car during persist but just update Id.
所以我试图通过这个实现的是:1)我可以使用详细的 JSON 视图阅读完整的汽车描述 2)或者我只能在简短的 JsonView 3 中阅读汽车的 Id),最重要的是,在创建我想要的新驱动程序时传递汽车的JSON ID。这样我就不需要在坚持期间为 Car 做 unnesesery 读取,而只需更新 Id。
Im getting following error: "object references an unsaved transient instance - save the transient instance before flushing : com.Driver.car -> com.Car"
我收到以下错误:“对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例:com.Driver.car -> com.Car”
I dont want to update instance of the Car in DB but rather just reference to it from Driver. Any idea how to achieve what I want?
我不想更新数据库中的 Car 实例,而只想从 Driver 引用它。知道如何实现我想要的吗?
Thank you.
谢谢你。
UPDATE: Forgot to mention that the ID of the Car that I pass during creation of the Driver is valid Id of the existing Car in DB.
更新:忘记提及我在创建驱动程序期间传递的汽车 ID 是数据库中现有汽车的有效 ID。
采纳答案by Tobb
That error message means that you have have a transient instance in your object graph that is not explicitly persisted. Short recap of the statuses an object can have in JPA:
该错误消息意味着您的对象图中有一个未显式持久化的瞬态实例。对象在 JPA 中可以具有的状态的简短回顾:
- Transient: A new object that has not yet been stored in the database (and is thus unknown to the entitymanager.) Does not have an id set.
- Managed: An object that the entitymanager keeps track of. Managed objects are what you work with within the scope of a transaction, and all changes done to a managed object will automatically be stored once the transaction is commited.
- Detached: A previously managed object that is still reachable after the transction commits. (A managed object outside a transaction.) Has an id set.
- Transient:尚未存储在数据库中的新对象(因此实体管理器不知道)。没有设置 id。
- Managed:entitymanager 跟踪的对象。托管对象是您在事务范围内使用的对象,一旦提交事务,对托管对象所做的所有更改都将自动存储。
- Detached:在事务提交后仍可访问的先前管理的对象。(事务外的托管对象。)有一个 id 集。
What the error message is telling you is that the (managed/detached) Driver-object you are working with holds a reference to a Car-object that is unknown to Hibernate (it is transient). In order to make Hibernate understand that any unsaved instances of Car being referenced from a Driver about be saved should also be saved you can call the persist-method of the EntityManager.
错误消息告诉您的是,您正在使用的(托管/分离的)Driver 对象包含对 Hibernate 未知的 Car 对象的引用(它是瞬态的)。为了让 Hibernate 理解从 Driver 引用的任何未保存的 Car 实例都应该被保存,你可以调用 EntityManager 的持久方法。
Alternatively, you can add a cascade on persist (I think, just from the top of my head, haven't tested it), which will execute a persist on the Car prior to persisting the Driver.
或者,您可以在persist上添加级联(我认为,只是从我的头顶来看,还没有测试过),它将在持久化驱动程序之前在汽车上执行持久化。
@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
@JoinColumn(name = "CAR_ID")
private Car car;
If you use the merge-method of the entitymanager to store the Driver, you should add CascadeType.MERGE
instead, or both:
如果您使用 entitymanager 的 merge-method 来存储驱动程序,则应CascadeType.MERGE
改为添加,或同时添加:
@ManyToOne(fetch=FetchType.LAZY, cascade={ CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name = "CAR_ID")
private Car car;
回答by user3619843
Use cascade in manytoone annotation @manytoone(cascade=CascadeType.Remove)
在manytoone注解中使用级联@manytoone(cascade=CascadeType.Remove)
回答by 00Enthusiast
public void setCarId(long carId) {
this.car = new Car (carId);
}
It is actually not saved version of a car
. So it is a transient objectbecause it hasn't id
. JPA demands that you should take care about relations. If entity is new (doesn't managed by context) it should be saved before it can relate with other managed/detached objects (actually the MASTER entity can maintain it's children by using cascades).
它实际上不是car
. 所以它是一个瞬态对象,因为它没有id
。JPA 要求您应该注意关系。如果实体是新的(不受上下文管理),则应在与其他托管/分离对象关联之前保存它(实际上,MASTER 实体可以通过使用级联来维护其子对象)。
Two ways: cascadesor save&retrievalfrom db.
两种方式:级联或从数据库中保存和检索。
Also you should avoid set entity ID
by hand. If you do not want to update/persist carby it's MASTER entity, you should get the CAR
from database and maintain your driverwith it's instance. So, if you do that, Car
will be detached from persistence context, BUT still it will have and ID and can be related with any Entity without affects.
此外,您应该避免ID
手动设置实体。如果您不想通过它的 MASTER 实体更新/保留汽车,您应该CAR
从数据库中获取并使用它的实例维护您的驱动程序。因此,如果您这样做,将与持久性上下文分离,但它仍然具有 ID 并且可以与任何实体相关而不会产生影响。Car
回答by scetix
You can do this via getReference
call in EntityManager
:
你可以通过getReference
调用来做到这一点EntityManager
:
EntityManager em = ...;
Car car = em.getReference(Car.class, carId);
Driver driver = ...;
driver.setCar(car);
em.persist(driver);
This will not do execute SELECT statement from the database.
这不会从数据库中执行 SELECT 语句。
回答by Radu Gancea
You can work only with the car
ID like this:
您只能使用这样的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;
回答by Jonck van der Kogel
As an answer to okutane, please see snippet:
作为对 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;
So what happens here is that when you want to do an insert/update, you only populate the carId field and perform the insert/update. Since the car field is non-insertable and non-updatable Hibernate will not complain about this and since in your database model you would only populate your car_id as foreign key anyway this is enough at this point (and your foreign key relationship on the database will ensure your data integrity). Now when you fetch your entity the car field will be populated by Hibernate giving you the flexibility where only your parent gets fetched when it needs to.
所以这里发生的事情是,当您想要进行插入/更新时,您只需填充 carId 字段并执行插入/更新。由于 car 字段是不可插入和不可更新的,Hibernate 不会对此抱怨,并且因为在您的数据库模型中,您只会将 car_id 填充为外键,无论如何这在这一点上就足够了(并且您在数据库上的外键关系将确保您的数据完整性)。现在,当您获取实体时,汽车字段将由 Hibernate 填充,从而使您可以灵活地在需要时仅获取您的父项。
回答by Youans
Add optional field equal false like following
添加可选字段等于 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;
That way you can set just car's id as
这样你就可以将汽车的 id 设置为
driver.setCar(new Car(1));
and then persist driver normal
然后坚持驱动正常
driverRepo.save(driver);
You will see that car with id 1 is assigned perfectly to driver in database
您将看到 ID 为 1 的汽车完美地分配给了数据库中的驱动程序
Description:
描述:
So what make this tiny optional=false
makes may be this would help more https://stackoverflow.com/a/17987718
那么是什么让这个微小的optional=false
制造可能是这将有助于更多https://stackoverflow.com/a/17987718