[英]object references an unsaved transient instance - save the transient instance before flushing : Spring Data JPA
I have Below 3 models : 我有以下3种型号:
Model 1: Reservation 模式一:预订
@Entity
public class Reservation {
public static final long NOT_FOUND = -1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
@OneToMany(mappedBy = "reservation", cascade = CascadeType.ALL, orphanRemoval = true)
public List<RoomReservation> roomReservations = new ArrayList<>();
}
Model 2: Room Reservation: 模式2:客房预订:
public class RoomReservation extends{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "RESERVATION_ID")
public Reservation reservation;
@OneToMany(mappedBy = "roomReservation", cascade = CascadeType.ALL, orphanRemoval = true)
public List<GuestDetails> guestDetails = new ArrayList<>();
}
Model 3 : Guest Details: 型号3:客人详细信息:
public class GuestDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
public Long guestId;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ROOM_RESERVATION_ID")
public RoomReservation roomReservation;
public Boolean isPrimary;
@Transient
public Guest guest;
}
The Relationship between those three are as : 这三个之间的关系如下:
Reservation --One to Many on RESERVATION_ID--> Room Reservation --One to Many on ROOM_RESERVATION_ID--> Guest Details 预订-一对一的RESERVATION_ID->房间预订-一对多的ROOM_RESERVATION_ID->客人详细信息
I am getting the reservation object and trying to update guest details i get the following error: 我正在获取预订对象并尝试更新访客详细信息,但出现以下错误:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.model.GuestDetails.roomReservation -> com.model.RoomReservation
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1760)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:82)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
... 73 common frames omitted
I have changed cascadeType to ALL as suggested in common question still getting the same error.Please donot make it duplicate as i have tried all the solution realated to this kind of question already asked 我已经按照常见问题中的建议将cascadeType更改为ALL,但仍然出现相同的错误。请不要重复,因为我已经尝试了针对已经问过的这种问题的所有解决方案
Please Let me know what mistake i am doing. 请让我知道我在做什么错。 Thanks
谢谢
Code to save Reservation Object by changing GuestDetails: 通过更改GuestDetails保存预订对象的代码:
Reservation existingReservation = reservationRepository.findOne(reservationId);
Reservation reservation = reservationParser.createFromJson(reservationNode);
existingReservation.roomReservations.forEach(roomReservation -> {
RoomReservation updatedRoomReservation = reservation.roomReservations.stream().filter(newRoomReservation -> Objects.equals(roomReservation.id, newRoomReservation.savedReservationId)).findFirst().orElse(null);
if(updatedRoomReservation != null){
roomReservation.guestDetails = updatedRoomReservation.guestDetails;
}
});
reservationRepository.save(existingReservation);
... save the transient instance before flushing :
com.model.GuestDetails.roomReservation -> com.model.RoomReservation
This exception states clearly that RoomReservation
contained in GuestDetails
, does not exist in the database (and most likely it's id
is null
). 该异常清楚地表明,
RoomReservation
包含的GuestDetails
在数据库中不存在(并且很可能其id
为null
)。
In general , you can solve this exception either by : 通常 ,您可以通过以下方法解决此异常:
Saving RoomReservation entity before saving GuestDetails 保存GuestDetails之前先保存RoomReservation实体
Or making cascade = CascadeType.ALL
(or at least {CascadeType.MERGE, CascadeType.PERSIST}
) for @ManyToOne
GuestDetail-->RoomReservation
或为
@ManyToOne
GuestDetail-->RoomReservation
cascade = CascadeType.ALL
(或至少{CascadeType.MERGE, CascadeType.PERSIST}
)
But first, I have a couple of points to cover: 但是首先,我要说明两点:
Do not use public fields in your class, this violates the encapsulation concept . 不要在您的课程中使用公共字段,这违反了封装概念 。
While you have a bidirectional association, you can set the other side of the association in your Setter
methods. 当您具有双向关联时,可以在
Setter
方法中设置关联的另一端。
For your case, you should change RoomReservation
class : 对于您的情况,您应该更改
RoomReservation
类:
public class RoomReservation{
//..... other lines of code
@OneToMany(mappedBy = "roomReservation", cascade = CascadeType.ALL, orphanRemoval = true)
private List<GuestDetails> guestDetails = new ArrayList<>();
public void setGuestDetails(List<GuestDetails> guestDetails) {
this.guestDetails.clear();
// Assuming that by passing null or empty arrays, means that you want to delete
// all GuestDetails from this RoomReservation entity
if (guestDetails == null || guestDetails.isEmpty()){
return;
}
guestDetails.forEach(g -> g.setRoomReservation(this));
this.guestDetails.addAll(guestDetails);
}
public List<GuestDetails> getGuestDetails() {
// Expose immutable collection to outside world
return Collections.unmodifiableList(guestDetails);
}
// You may add more methods to add/remove from [guestDetails] collection
}
Saving the Reservation: 保存预订:
Reservation existingReservation = reservationRepository.findOne(reservationId);
Reservation reservation = reservationParser.createFromJson(reservationNode);
existingReservation.roomReservations.forEach(roomReservation -> {
Optional<RoomReservation> updatedRoomReservation = reservation.roomReservations.stream().filter(newRoomReservation -> Objects.equals(roomReservation.id, newRoomReservation.savedReservationId)).findFirst();
if(updatedRoomReservation.isPresent()){
// roomReservation already exists in the database, so we don't need to save it or use `Cascade` property
roomReservation.setGuestDetails( updatedRoomReservation.get().getGuestDetails());
}
});
reservationRepository.save(existingReservation);
Hope it helps! 希望能帮助到你!
GuestDetails
- add the needed CasadeType: GuestDetails
添加所需的CasadeType:
@ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
@JoinColumn(name = "ROOM_RESERVATION_ID")
public RoomReservation roomReservation;
RoomReservation - add the nedded CascadeType: RoomReservation-添加所需的CascadeType:
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.AL)
@JoinColumn(name = "RESERVATION_ID")
public Reservation reservation;
And then you need to persist the data before/after using the for-each loop. 然后,您需要在使用for-each循环之前/之后保留数据。 Depends on you
safe()
-Method. 取决于您
safe()
Method。
Reservation reservation = reservationParser.createFromJson(reservationNode);
entityManager.persist(reservation);
And then safe it afterwards. 然后将其安全。 Tell me your result.
告诉我你的结果。 Maybe directly working without changing/adding the cascadetypes.
也许可以直接工作而无需更改/添加级联类型。
You can save the reservation you get from the Json. 您可以保存从Json获得的预订。 JPA will update the rows with the same id's.
JPA将更新具有相同ID的行。
The error you get is because the guestDetails has still a reference to the updatedRoomReservation. 您收到的错误是因为guestDetails仍具有对updatedRoomReservation的引用。 If you don't want to save the whole reservation from the json you have to set the right RoomReservation.
如果您不想从json保存整个预订,则必须设置正确的RoomReservation。
eg: 例如:
if(updatedRoomReservation != null){
roomReservation.guestDetails = updatedRoomReservation.guestDetails;
guestDetails.forEach(guestDetail -> guestDetail.roomReservation = roomReservation);
}
If you are using JPA 2.0 then defaults fetch type for OneToMany is LAZY. 如果您使用的是JPA 2.0,则OneToMany的默认获取类型为LAZY。 If after your lambda, your
updatedRoomReservation
is null (as you set in orElse) then existingReservation.roomReservation.guestDetails
will never be loaded and will be null. 如果你的拉姆达后,您
updatedRoomReservation
为空 (如您在设置否则容易),那么existingReservation.roomReservation.guestDetails
将永远不会被加载,并会为空。
Therefore when you save existingReservation
, you get the error. 因此,当您保存
existingReservation
,您会收到错误消息。
This can be caused by incorrect transaction semantics. 这可能是由错误的事务语义引起的。
If the referenced instance was not fetched in the current transaction it counts as transient. 如果在当前事务中未获取引用的实例,则将其视为瞬态。
The easiest solution is to add @Transactional
to the method: 最简单的解决方案是将
@Transactional
添加到方法中:
@Transactional
public void saveReservation(...) {
Reservation existingReservation = reservationRepository.findOne(reservationId);
Reservation reservation = reservationParser.createFromJson(reservationNode);
// ...
reservationRepository.save(existingReservation);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.