The object refers to an unsaved instance of the transient — save the temporary instance before flushing: Spring JPA data

I have below 3 models:

Model 1: Booking

@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: Details for guests:

 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 relationships between the three are as follows:

Reservation - from one to many at RESERVATION_ID → Booking a room - from one to many at ROOM_RESERVATION_ID → Information for guests

I get the reservation object and try to update the guest information, 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 changed cascadeType to EVERYTHING as suggested in the general question, still getting the same error. Please do not duplicate, because I tried all the solutions that were resolved on this already asked question

Please let me know what mistake I am making. Thanks

Code to save the reservation object by modifying 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); 
+7
java spring spring-boot spring-data-jpa hibernate
source share
4 answers

GuestDetails - add the required CasadeType style:

 @ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL) @JoinColumn(name = "ROOM_RESERVATION_ID") public RoomReservation roomReservation; 

RoomReservation - add nedded CascadeType:

 @JsonIgnore @ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.AL) @JoinColumn(name = "RESERVATION_ID") public Reservation reservation; 

And then you need to save the data before / after using the for-each loop. safe() -Method is up to you.

 Reservation reservation = reservationParser.createFromJson(reservationNode); entityManager.persist(reservation); 

And then it's safe then. Tell me your result. Maybe directly works without modifying / adding cascadetypes.

+1
source share
 ... save the transient instance before flushing : com.model.GuestDetails.roomReservation -> com.model.RoomReservation 

This exception explicitly states that the RoomReservation contained in GuestDetails does not exist in the database (and most likely its id is null ).

In general, you can solve this exception either:

  • Saving a Number Preservation Object Before Saving GuestDetails

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

But first, I have a few points to cover:

  • Do not use public fields in your class, this violates the concept of encapsulation .

  • As long as you have bidirectional communication, you can set the other side of the association in your Setter methods.

In your case, you should change the 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 a 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 this helps!

+1
source share

You can save the reservation you receive from Json. JPA will update rows with the same identifiers.

The error you are getting is due to guestDetails still referring to an updated version of RoomReservation. If you do not want to keep the entire backup from json, you need to install the correct RoomReservation.

eg:

 if(updatedRoomReservation != null){ roomReservation.guestDetails = updatedRoomReservation.guestDetails; guestDetails.forEach(guestDetail -> guestDetail.roomReservation = roomReservation); } 
0
source share

If you are using JPA 2.0, then the default fetch type for OneToMany LAZY. If after your lambda your updatedRoomReservation null (as you set in orElse), then existingReservation.roomReservation.guestDetails will never be loaded and will be empty.

Therefore, when you save existingReservation , you get an error.

0
source share

All Articles