2017-10-20 48 views
7

J'ai moins de 3 modèles:fait référence à une instance transitoire non enregistrées - sauvegarder l'instance transitoire avant le rinçage: Spring données JPA

Modèle 1: Réservation

@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<>(); 
} 

Modèle 2: Chambre Réservation:

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

Modèle 3: Détails des clients:

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; 

} 

Les relations entre ces trois sont comme:

Réservation --Une Many sur RESERVATION_ID -> Chambre Réservation --Une Many sur ROOM_RESERVATION_ID -> Invité Détails

Je reçois le objet de réservation et d'essayer de mettre à jour les détails d'invité i l'erreur suivante:

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 

J'ai changé CascadeTypes à tous comme suggéré en question commune obtenir toujours le même error.Please donot faire en double comme j'ai essayé toute la solution réalisée à ce genre de question déjà posée

S'il vous plaît laissez-moi savoir quelle erreur je fais. Merci

Code pour sauvegarder l'objet de réservation en changeant 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

Veuillez écrire le code qui enregistre réellement le type de classe que vous essayez d'enregistrer. – PaulNUK

+0

@PaulNUK J'ai ajouté un bloc de code que j'utilise pour sauvegarder. Veuillez jeter un oeil. –

+0

@PaulNUK Salut, des mises à jour ..? –

Répondre

1

GuestDetails - ajouter CasadeType nécessaire:

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

RoomReservation - ajouter CascadeTypes nedded:

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

Ensuite, vous devez conserver les données avant/après l'utilisation de la boucle for-each. Cela dépend de vous safe() -Méthode.

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

Ensuite, sauvez-le ensuite. Dites-moi votre résultat. Peut-être travailler directement sans changer/ajouter les cascadetypes.

+0

J'ai essayé d'ajouter cascadeType.ALL aux détails de l'invité, et l'objet persistant après la boucle recevant toujours la même erreur. –

0

Vous pouvez enregistrer la réservation que vous recevez du Json. JPA mettra à jour les lignes avec les mêmes ID. L'erreur que vous obtenez est parce que guestDetails a toujours une référence à la propriété updatedRoomReservation. Si vous ne voulez pas enregistrer la totalité de la réservation depuis le JSON, vous devez définir la bonne RoomReservation.

.: par exemple

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

Si vous utilisez JPA 2.0 alors par défaut pour type d'extraction OneToMany est LAZY.Si après votre lambda, votre updatedRoomReservation est null (comme vous l'avez défini dans orElse) alors existingReservation.roomReservation.guestDetails ne sera jamais chargé et sera null.

Par conséquent lorsque vous enregistrez existingReservation, vous obtenez l'erreur.

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

Cette Etats exception clairement que RoomReservation contenus dans GuestDetails, n'existe pas dans la base de données (et il est plus probable id est null).

En générale, vous pouvez résoudre cette exception soit par:

  • Saving entité RoomReservation avant d'enregistrer GuestDetails

  • Ou faire des cascade = CascadeType.ALL (ou au moins {CascadeType.MERGE, CascadeType.PERSIST}) pour @ManyToOneGuestDetail-->RoomReservation

Bu t d'abord, j'ai deux ou trois points à couvrir:

  • Ne pas utiliser les champs publics dans votre classe, cela viole the encapsulation concept. Lorsque vous disposez d'une association bidirectionnelle, vous pouvez définir l'autre côté de l'association dans vos méthodes Setter.

Pour votre cas, vous devez changer RoomReservation classe:

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 
} 

Enregistrement de la réservation:

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!