【问题标题】:JPA OneToMany with Parent composite pk is part of child primary key Derived Entity issue具有父复合 pk 的 JPA OneToMany 是子主键派生实体问题的一部分
【发布时间】:2021-11-09 00:03:09
【问题描述】:

一对多与父复合主键是子主键问题的一部分。使用以下代码 sn-ps 引发异常

示例 JSON 嵌入数据如下:

{
"pt_reg_no": "1000", //序列号生成
"game_year": "G12021",
“名字”:“我的名字”,
“事件详情”:[{ "major_event_code": "A", "sub_event_code": "A7", “category_code”:“MO” }, { "major_event_code": "B", "sub_event_code": "B7", “category_code”:“WO” } ]
}

参与者可以注册多个活动:
参与者(复合键) - pt_reg_no, game_year
EventDetails(复合键) - 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");
    }

【问题讨论】:

    标签: java jpa one-to-many composite


    【解决方案1】:

    您可以通过像这样映射您的详细信息类来尝试“派生身份”:

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

    在第 2.4.1 节的JPA 2.2 spec 中讨论了派生的身份(带有示例)。

    【讨论】:

    • ParticipantModel [pt_reg_no=1000, game_year=G12021,name=bapuji, eventDetails=[EventDetailsModel [major_event_code=A, state_abbr=null, sub_event_code=A7, category_code=MO]]] 谢谢@Brian下面的异常嵌套异常是 org.hibernate.HibernateException: No part of a complex identifier may be null] 有根本原因
    • @BapujiNakka,可能是您调用participant.getEventDetails() 返回的参与者详细信息不包含指向ParticipantModel 的反向指针(即EventDetilasModel.participantModelnull)。
    • 是的@Brian,它只为空,如下所示:- ParticipantModel [pt_reg_no=null, game_year=202126, name=Bapuji, eventDetails=[EventDetailsModel [major_event_code=A, state_abbr=null, sub_event_code=A7 , category_code=MO ,participantModel=null]]] 1)如何将反向指针设置为父级。在我的问题 ABOVE 中更新了 getter 和 setter。你能看看有什么问题吗? 2) 父级中的 pt_reg_no 序列号也由于某些冲突而没有生成
    【解决方案2】:

    OneToMany 映射的替代解决方案父级具有复合键并且父级也需要生成序列号

    在设置或列出子实体时使用 @Transient 注释
    1) 使用上述注释保存父实体有助于 jpa 从保存中跳过 EventsDetailsModel 列表/集
    2) 迭代 Child 并获取已保存的 Parent 复合键值,并将子数据与子键属性一起持久化。

    //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");
        }
    
    

    【讨论】:

      猜你喜欢
      • 2021-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 2011-02-13
      • 1970-01-01
      • 2015-10-14
      相关资源
      最近更新 更多