Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

object references an unsaved transient instance - save the transient instance before flushing : Spring Data JPA

I have Below 3 models :

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:

 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:

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

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

Please Let me know what mistake i am doing. Thanks

Code to save Reservation Object by changing 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);
like image 912
utsav anand Avatar asked Oct 20 '17 11:10

utsav anand


1 Answers

... 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).

In general, you can solve this exception either by :

  • Saving RoomReservation entity before saving GuestDetails

  • Or making cascade = CascadeType.ALL (or at least {CascadeType.MERGE, CascadeType.PERSIST}) for @ManyToOne GuestDetail-->RoomReservation

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.

For your case, you should change RoomReservation class :

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!

like image 118
O.Badr Avatar answered Sep 18 '22 23:09

O.Badr