Home > Software design >  JPA OneToMany with Parent composite pk is part of child primary key Derived Entity issue
JPA OneToMany with Parent composite pk is part of child primary key Derived Entity issue

Time:09-22

One To Many with Parent composite primary key is part of child primary key issue. Throwing Exceptions with the below code snippets

Sample JSON Embedded Data as follows:

{
"pt_reg_no": "1000", //Serial number generation
"game_year": "G12021",
"name": "myname",
"eventDetails": [{ "major_event_code": "A", "sub_event_code": "A7", "category_code": "MO" }, { "major_event_code": "B", "sub_event_code": "B7", "category_code": "WO" } ]
}

Participant can register for multiple Events:
Participant (Composite Key) - pt_reg_no, game_year
EventDetails (Composite Key) - pt_reg_no, game_year, sub_event_code, category_code

//Parent Class IDclass
public class ParticipantKey implements Serializable {

    private Long pt_reg_no;
    private String game_year;
}

@IdClass(ParticipantKey.class)
public class ParticipantModel { 

    @Id
    @GeneratedValue(
        strategy = GenerationType.SEQUENCE,
        generator = "participantseq"
    )
    @SequenceGenerator(
        name = "participantseq",
        allocationSize = 1
    )
    private Long pt_reg_no;
    
    @Id
    private String game_year = 'G12021';
    
    
    @OneToMany(mappedBy = "participantModel", orphanRemoval = true, cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    private Set<EventDetailsModel> eventDetails = new HashSet<>(); 

    public Set<EventDetailsModel> getEventDetails() {
        return eventDetails;
    }   
    public void setEventDetails(Set<EventDetailsModel> eventDetails) {
        this.eventDetails = eventDetails;

        for (EventDetailsModel b : eventDetails) {
            b.setParticipantModel(this);
        }
    }
    
}
    
//Child class IDclass    
public class ParticipantEventDetailsId implements Serializable {
        private ParticipantKey participantModel;
        private String sub_event_code;
        private String category_code;
    }
    
public class EventDetailsModel { 
      @Id    
      @Column(name = "sub_event_code")
      private String sub_event_code;
        
      @Id
      @Column(name = "category_code")
      private String category_code;
        
      @Id
      @ManyToOne(cascade = CascadeType.ALL)
      @JoinColumns({
         @JoinColumn(name = "pt_reg_no", referencedColumnName = "pt_reg_no"),
          @JoinColumn(name = "game_year", referencedColumnName = "game_year")
      })
      private ParticipantModel participantModel;


    public ParticipantModel getParticipantModel() {
        return participantModel;
    }
    public void setParticipantModel(ParticipantModel participantModel) {
        this.participantModel = participantModel;
    }
    }
    
//---------------------------------------------------
//JSON input data received into ParticipantModel from @requestBody
public class FormDataObjects {
    private ParticipantModel formData;

    @JsonCreator
    public FormDataObjects(ParticipantModel formData) {
        this.formData = formData;
    }
}

//Controller Entry point
@RequestMapping(path = "/participantData", method = RequestMethod.POST)
    @Transactional
    public ResponseEntity<?> participantRegistration(@Valid @RequestBody FormDataObjects values) {
        ParticipantModel participant = values.getFormData();
        participant.setGame_year(new SimpleDateFormat("yyyy").format(new Date())   GenricData.game);
     
        ParticipantModel person = participantRepository.save(participant);
        
                if (person == null) {
            return ResponseEntity.ok("Participant is not inserted");
        } else {
            Set<EventDetailsModel> eventDetails = participant.getEventDetails();
            if (eventDetails.size() > 0) {
                eventDetails.forEach(event -> {
                    
                    //event.setPt_reg_no(person.getPt_reg_no());
                    //event.setGame_year(person.getGame_year());
                    //event.setParticipantModel(participant);
                    entityManager.persist(event);
                    entityManager.flush();
                    entityManager.clear();
                });

            }

        }

        return ResponseEntity.ok("inserted");
    }

CodePudding user response:

You can try a "derived identity" by mapping your details class like this:

public class ParticipantEventDetailsId implements Serializable {
    private String sub_event_code;
    private String category_code;
    private ParticipantKey participantModel; // matches name of attribute and type of ParticipantModel PK
}
    
public class EventDetailsModel { 

    @Id
    @Column(name = "sub_event_code")
    private String sub_event_code;

    @Id
    @Column(name = "category_code")
    private String category_code;

    @Id
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumns({
        @JoinColumn(name = "pt_reg_no", referencedColumnName = "pt_reg_no"),
        @JoinColumn(name = "game_year", referencedColumnName = "game_year")
    })
    private ParticipantModel participantModel;

}

Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.

CodePudding user response:

Alternative solution to OneToMany mapping when parent has composite key and serial number generation is also required at parent

Use @Transient annotation at Set or List Child Entities
1)save Parent entity with above annotation helps jpa to skip List/Set of EventsDetailsModel from saving
2) Iterate Child and get Parent composite key values which are already saved and persist child data along with childs key attributes.

//Parent Class IDclass
public class ParticipantKey implements Serializable {

    private Long pt_reg_no;
    private String game_year;
}

@IdClass(ParticipantKey.class)
public class ParticipantModel { 

    @Id
    @GeneratedValue(
        strategy = GenerationType.SEQUENCE,
        generator = "participantseq"
    )
    @SequenceGenerator(
        name = "participantseq",
        allocationSize = 1
    )
    private Long pt_reg_no;
    
    @Id
    private String game_year = 'G12021';
    
    @Transient  //Find exact path for Transient
    private Set<EventDetailsModel> eventDetails = new HashSet<>(); 

    public Set<EventDetailsModel> getEventDetails() {
        return eventDetails;
    }   

    
}
    
//Child class IDclass    
public class ParticipantEventDetailsId implements Serializable {
        private Long pt_reg_no;
        private String game_year;
        private String sub_event_code;
        private String category_code;
    }
    
public class EventDetailsModel { 
      @Id    
      @Column(name = "sub_event_code")
      private String sub_event_code;
        
      @Id
      @Column(name = "category_code")
      private String category_code;
        
      @Id
      private Long pt_reg_no;

      @Id
      private String game_year;
  
    }
    
//---------------------------------------------------
//JSON input data received into ParticipantModel from @requestBody
public class FormDataObjects {
    private ParticipantModel formData;

    @JsonCreator
    public FormDataObjects(ParticipantModel formData) {
        this.formData = formData;
    }
}

//Controller Entry point
@RequestMapping(path = "/participantData", method = RequestMethod.POST)
    @Transactional
    public ResponseEntity<?> participantRegistration(@Valid @RequestBody FormDataObjects values) {
        ParticipantModel participant = values.getFormData();
        participant.setGame_year(new SimpleDateFormat("yyyy").format(new Date())   GenricData.game);
     
        ParticipantModel person = participantRepository.save(participant);
        
                if (person == null) {
            return ResponseEntity.ok("Participant is not inserted");
        } else {
            Set<EventDetailsModel> eventDetails = participant.getEventDetails();
            if (eventDetails.size() > 0) {
                eventDetails.forEach(event -> {
                    
                    event.setPt_reg_no(person.getPt_reg_no());
                    event.setGame_year(person.getGame_year());
                    entityManager.persist(event);
                    entityManager.flush();
                    entityManager.clear();
                });

            }

        }

        return ResponseEntity.ok("inserted");
    }

  • Related