简体   繁体   English

对象引用了一个未保存的瞬态实例-在刷新之前保存瞬态实例:Spring Data JPA

[英]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在数据库中不存在(并且很可能其idnull )。

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.

相关问题 Spring Data/JPA 和 errorType :对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例 - Spring Data/JPA and errorType : object references an unsaved transient instance - save the transient instance before flushing JPA对象引用了未保存的瞬态实例-在刷新之前保存瞬态实例 - JPA object references an unsaved transient instance - save the transient instance before flushing 对象引用了未保存的瞬态实例-在刷新休眠JPA之前保存瞬态实例 - Object references an unsaved transient instance - save the transient instance before flushing hibernate JPA 保存实体时JPA错误,对象引用了未保存的瞬态实例-在刷新之前保存瞬态实例 - JPA error while saving entity, object references an unsaved transient instance - save the transient instance before flushing 数据未保存:对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例 - Data was not saved: object references an unsaved transient instance - save the transient instance before flushing 使用 Hibernate 保存 object object 引用未保存的瞬态实例 在刷新之前保存瞬态实例 - save the object using Hibernate object references an unsaved transient instance save the transient instance before flushing 如何解决对象引用未保存的瞬态实例的错误-在刷新之前保存瞬态实例? - How do I solve this error of object references an unsaved transient instance - save the transient instance before flushing? 对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例:com.entity.Role - object references an unsaved transient instance - save the transient instance before flushing: com.entity.Role TransientObjectException:对象引用了一个未保存的临时实例-在执行合并时在刷新之前保存该临时实例 - TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing when I am doing merge 对象引用了一个未保存的临时实例-使用休眠空间在刷新之前保存该临时实例 - object references an unsaved transient instance - save the transient instance before flushing using hibernate spatial
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM