2017-10-20 105 views
7

mam poniżej 3 modeli:obiekt odwołuje niezapisaną przemijające wystąpienie - zachowaj przemijające instancji przed spłukiwanie: Wiosna danych WZP

Model 1: Rezerwacja

@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: Pokój Rezerwacja:

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: Szczegóły gości:

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; 

} 

wzajemny stosunek tych trzech są:

Rezerwacja --One wielu na RESERVATION_ID -> Pokój Rezerwacja --One wielu na ROOM_RESERVATION_ID -> Szczegóły gości

otrzymuję celem rezerwacji i próbuje zaktualizować dane gościnnie pojawia się następujący błąd:

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 

Zmieniłem cascadeType ALL jak sugeruje częste pytanie wciąż otrzymuję ten sam error.Please dawca uczynić go powielać tak jak wypróbowałem wszystkie rozwiązania wykonane na tego rodzaju pytanie już zadane

Proszę dać mi znać, jaki błąd robię. Dzięki

Kod zapisać Rezerwacja obiektu za zmieniającymi 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); 
+1

Proszę zaksięgować kod, który faktycznie robi zapisywanie - jakiego typu klasy próbujesz zapisać? – PaulNUK

+0

@PaulNUK Dodałem blok kodu, którego używam do zapisania. Proszę spojrzeć. –

+0

@PaulNUK Witam, jakieś aktualizacje ..? –

Odpowiedz

1

GuestDetails - dodaj potrzebne CasadeType:

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

RoomReservation - dodać nedded CascadeType:

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

A następnie musisz utrwalić dane przed/po użyciu pętli for-each. Zależy od ciebie safe() -Metoda.

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

A następnie bezpiecznie. Powiedz mi swój wynik. Może działa bezpośrednio, bez zmiany/dodawania kodów kaskadowych.

+0

Próbowałem dodać cascadeType.ALL do szczegółów gościa, a trwający obiekt po pętli wciąż otrzymuje ten sam błąd. –

0

Możesz zapisać rezerwację, którą otrzymałeś od Json. JPA zaktualizuje wiersze o tym samym identyfikatorze.

Błąd, który otrzymujesz, ponieważ guestDetails ma wciąż odniesienie do updatedRoomReservation. Jeśli nie chcesz zapisać całej rezerwacji od json, musisz ustawić odpowiednią rezerwację pokoju.

np .:

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

Jeśli używasz JPA 2.0 wtedy domyślnie pobrać typ dla OneToMany jest leniwy.Jeśli po twojej lambda, twój updatedRoomReservation jest ustawiony na orElse, to nigdy nie zostanie załadowany i będzie mieć wartość null.

Dlatego po zapisaniu existingReservation pojawia się błąd.

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

wyjątek ten stwierdza wyraźnie, że zawarte w GuestDetailsRoomReservation, nie istnieje w bazie danych (i najprawdopodobniej to id jest null).

W ogólnym, można rozwiązać ten wyjątek poprzez:

  • Saving RoomReservation podmiotu przed zapisaniem GuestDetails

  • lub dokonujących cascade = CascadeType.ALL (lub przynajmniej {CascadeType.MERGE, CascadeType.PERSIST}) dla @ManyToOneGuestDetail-->RoomReservation

Bu t pierwsze, mam kilka punktów na pokrycie:

  • Nie używaj pola publiczne w swojej klasie, to jest niezgodny the encapsulation concept.

  • Chociaż masz powiązanie dwukierunkowe, możesz ustawić drugą stronę skojarzenia w swoich metodach Setter.

Dla Państwa przypadku należy zmienić RoomReservation Klasa:

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 
} 

Zapisywanie rezerwacji:

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

Nadzieję, że to pomaga!