JPA双向@OneToMany不在引用表上填充外键

huangapple go评论61阅读模式
英文:

JPA bi-directional @OneToMany not populating Foreign Key on reference table

问题

I have two tables "Action" and "Action_Function" where the latter has a Composite Primary Key (Action_id & Sequence_nr). But when I execute repository.save(action), the value of the "Action_id" column in the "Action_Function" table inserts as null.

I am using Spring Boot 3 with Spring Data JPA (Hibernate).

Action:

Action_id (pk)

Action_Function:

Action_id (pk)
Sequence_nr (pk) <-- Sequence_nr comes from UI (DTO)

I am using @IdClass for the convenience of MapStruct DTO to entity mapping, hence not using @Embeddable.

@Entity
@Table(name = "ACTION")
public class Action {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "Action_id")
    private Integer actionId;

    @OneToMany(mappedBy = "action", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<ActionFunction> actionFunctions = new HashSet<>();

    // Other fields
}
@Entity
@Table(name = "ACTION_FUNCTION")
@IdClass(ActionFunctionId.class)
public class ActionFunction {

    @Id
    @Column(name = "Action_id")
    private Integer actionId;

    @Id
    @Column(name = "Sequence_nr")
    private Integer sequenceNr;

    // here, added insertable and updatable false otherwise JPA tries to add two Action_id 
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = "Action_id")
    @MapsId("actionId")
    private Action action;

    // other fields
}
public class ActionFunctionId implements Serializable {

    @Serial
    private static final long serialVersionUID = -4601022716234244483L;

    private Integer actionId;

    private Integer sequenceNr;

    // Equals & hashCode
}

Here is how I am populating entities, a simple representation of my current code where they get populated using MapStruct.

@Service
public class ActionServiceImpl {

    public void saveAction(){
        ActionFunction actionFunction = new ActionFunction();
        actionFunction.setSequenceNr(1);

        ActionFunction actionFunction2 = new ActionFunction();
        actionFunction2.setSequenceNr(2);

        Action action = new Action();
        action.setActionFunctions(Set.of(actionFunction, actionFunction2));
        Action actionSaved = actionRepository.save(action);
    }
}

ActionRepository is a simple Spring Data JPA Repository. We are following DDD, and these two entities are part of the same Aggregate; hence, we can have one Repository with the root aggregate Action.

@Repository
public interface ActionRepository extends JpaRepository<Action, Integer> {
    
}

I appreciate some advice on how to fix this.

英文:

I have two tables "Action" and "Action_Function" where later has Composite Primary Key (Action_id & Sequence_nr).
But, when I execute repository.save(action) the value Action_id column on Action_Function table insert as null.

I am using Spring Boot 3 with Spring Data JPA(Hibernate).

Action

Action_id (pk)

Action_Function

Action_id (pk)
Sequence_nr (pk) <-- Sequence_nr comes from UI(DTO)

I am using @IdClass for the convenience of MapStruct DTO to entity mapping, hence not using @Embeddable.

@Entity
@Table(name = &quot;ACTION&quot;)
public class Action {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = &quot;Action_id&quot;)
    private Integer actionId;

    @OneToMany(mappedBy = &quot;action&quot;, fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private Set&lt;ActionFunction&gt; actionFunctions = new HashSet&lt;&gt;();

    // Other fields
}
@Entity
@Table(name = &quot;ACTION_FUNCTION&quot;)
@IdClass(ActionFunctionId.class)
public class ActionFunction {

    @Id
    @Column(name = &quot;Action_id&quot;)
    private Integer actionId;

    @Id
    @Column(name = &quot;Sequence_nr&quot;)
    private Integer sequenceNr;

    // here, added insertable and updatable false otherwise JPA tries to add two Action_id 
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = &quot;Action_id&quot;)
    @MapsId(&quot;actionId&quot;)
    private Action action;

    // other fields
}
public class ActionFunctionId implements Serializable {

    @Serial
    private static final long serialVersionUID = -4601022716234244483L;

    private Integer actionId;

    private Integer sequenceNr;

    // Equals &amp; hashCode
}

Here is how I am populating entities, a simple representation of my current code where they get populated using MapStruct.

@Service
public class ActionServiceImpl {

        public void saveAction(){
            ActionFunction actionFunction = new ActionFunction();
            actionFunction.setSequenceNr(1);

            ActionFunction actionFunction2 = new ActionFunction();
            actionFunction2.setSequenceNr(2);

            Action action = new Action();
            action.setActionFunctions(Set.of(actionFunction, actionFunction2));
            Action actionSaved = actionRepository.save(action);
        }
    }

ActionRepository is simple Spring Data Jpa Repository. We are following DDD and these two entities are part of same Aggregate, hence we can have one Repository with root aggregate Action.

@Repository
 public interface ActionRepository extends JpaRepository&lt;Action, Integer&gt; {
        
        }

I really appreciate some advice how to fix this.

答案1

得分: 0

JPA新增了选项,允许在子实体中从父实体派生ID。要使子实体将父实体的ID作为其复合ID的一部分使用,只需使用@MapsId注释标记关系:

@Entity
@Table(name = "ACTION_FUNCTION")
@IdClass(ActionFunctionId.class)
public class ActionFunction {

    @Id
    @Column(name = "Action_id")
    private Integer actionId;

    @Id
    @Column(name = "Sequence_nr")
    private Integer sequenceNr;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
    // 这将使用action.id的值由JPA设置actionId属性
    @MapsId("actionId")
    private Action action;

    // 其他字段
}
英文:

JPA added options to allow deriving the ID in a child entity from the parent. To have your child use the parent's ID as part of its compound ID, you just need to mark the relationship with the MapsId annotation:

@Entity
@Table(name = &quot;ACTION_FUNCTION&quot;)
@IdClass(ActionFunctionId.class)
public class ActionFunction {

    @Id
    @Column(name = &quot;Action_id&quot;)
    private Integer actionId;

    @Id
    @Column(name = &quot;Sequence_nr&quot;)
    private Integer sequenceNr;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
    //This will have JPA set the actionId property using the action.id value
    @MapsId(&quot;actionId&quot;)
    private Action action;

    // other fields
}

答案2

得分: 0

终于,我找到了一个有效的解决方案。

@Entity
@Table(name = "ACTION")
public class Action implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Action_id")
private Integer actionId;

@OneToMany(mappedBy = "action", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<ActionFunction> actionFunctions = new HashSet<>();

}


@Entity
@Table(name = "ACTION_FUNCTION", schema = "dbo")
@IdClass(ActionFunctionId.class)
public class ActionFunction implements Serializable {

@Id
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "Action_id", referencedColumnName = "Action_id")
private Action action;

@Id
@Column(name = "Sequence_nr")
private Integer sequenceNr;

}


@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
public class ActionFunctionId implements Serializable {

private Integer action;

private Integer sequenceNr;

}


**JUnit 测试**

@Test
void createAction(){

ActionFunction actionFunction = new ActionFunction();
actionFunction.setSequenceNr(1);

Action action = new Action();
action.setActionFunctions(Set.of(actionFunction));

actionFunction.setAction(action);

Action actionSaved = actionRepository.save(action);

}


[数据库记录][1]

  [1]: https://i.stack.imgur.com/KI3Ra.png
英文:

Finally, I have found a working solution.

@Entity
@Table(name = &quot;ACTION&quot;)
public class Action implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = &quot;Action_id&quot;)
    private Integer actionId;

    @OneToMany(mappedBy = &quot;action&quot;, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set&lt;ActionFunction&gt; actionFunctions = new HashSet&lt;&gt;();
}
@Entity
@Table(name = &quot;ACTION_FUNCTION&quot;, schema = &quot;dbo&quot;)
@IdClass(ActionFunctionId.class)
public class ActionFunction implements Serializable {

    @Id
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = &quot;Action_id&quot;, referencedColumnName = &quot;Action_id&quot;)
    private Action action;

    @Id
    @Column(name = &quot;Sequence_nr&quot;)
    private Integer sequenceNr;
}
@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
public class ActionFunctionId implements Serializable {

    private Integer action;

    private Integer sequenceNr;
}

JUnit Test

    @Test
    void createAction(){

        ActionFunction actionFunction = new ActionFunction();
        actionFunction.setSequenceNr(1);

        Action action = new Action();
        action.setActionFunctions(Set.of(actionFunction));

        actionFunction.setAction(action);

        Action actionSaved = actionRepository.save(action);
    }

Records on database

huangapple
  • 本文由 发表于 2023年5月10日 11:53:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76214773.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定