在Spring Data JPA中保留两个不同模式之间具有父子关系的表

时间:2017-05-01 09:21:17

标签: java sql-server hibernate spring-data-jpa jpa-2.0

我想做,

ChildTable child = new ChildTable();
clild.setParentTwoID(prentTwoID);
ParentOne parentOne = new ParentOne();
parentOne.getChildTableList().add(childTable);
parentReposetory.save(parentOne);

其中,ParentOne位于架构S1 ,ParentTwo与架构S2 中的ChildTable一起。

ParentOne.java

@Data
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(name = "ParentOne", schema = "S1")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, scope = ParentOne.class)
public class ParentOne implements Serializable {

    private static final long serialVersionUID = 7502273069461829133L;

    @Id
    @Column(name = "ParentOneID", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer parentOneID;

    @OneToMany(targetEntity = ChildTable.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "ParentOneID", nullable = false, insertable = false, updatable = false)
    private List<ChildTable> childTableList;
}

ParentTwo.java

@Data
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(name = "ParentTwo", schema = "S2")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, scope = ParentTwo.class)
public class ParentTwo implements Serializable {

    private static final long serialVersionUID = 7502273069461829133L;

    @Id
    @Column(name = "ParentTwoID", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer parentTwoID;

    @OneToMany(targetEntity = ChildTable.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "ParentTwoID", nullable = false, insertable = false, updatable = false)
    private List<ChildTable> childTableList;
}

ChildTable.java

@Data
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(name = "ChildTable", schema = "S2")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, scope = ChildTable.class)
public class ChildTable implements Serializable {

    private static final long serialVersionUID = -6331600489988183852L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ChildTableID", unique = true, nullable = false)
    private Integer childTableID;

    @Column(name = "ParentOneID")
    private Integer parentOneID;

    @Column(name = "ParentTwoID", nullable = false)
    @NotNull(message = "mandatory_field")
    private Integer parentTwoID;

}

这里ParentTwo已经存在,我有它的ID。我将继续使用不同架构的ChildTable继续使用ParentOne。当我这样做时,我得到错误说。

  

rg.springframework.dao.DataIntegrityViolationException:不能   执行声明; SQL [不适用];约束[null];嵌套异常是   org.hibernate.exception.ConstraintViolationException:不能   执行声明

     

引起:com.microsoft.sqlserver.jdbc.SQLServerException:不能   将值NULL插入列&#39; ParentOneID&#39;,表中   &#39; PT_Sample.S2.ChildTable&#39 ;;列不允许空值。 INSERT失败。

     

SQL错误:515,SQLState:23000

1 个答案:

答案 0 :(得分:2)

使用JPA时,需要允许JPA提供程序管理实体实例之间的关系。在给定的情况下,通过保持跟踪各种外键标识符来手动管理关系。因此,JPA提供程序无法设置外键列值。

基本问题似乎在于了解JPA(或任何其他ORM)的工作原理。实体不是数据库表的逐列副本。需要将它们想象并建模为对象而不考虑底层数据库。实体属性到数据库列的映射应留给JPA提供者。

以下实体模型将解决问题(为简洁起见,删除了所有不必要的代码):

  

ParentOne 实体

@Entity
@Table(name="ParentOne", schema="S1")
public class ParentOne {
  @Id
  @Column(name="ParentOneID", unique=true, nullable=false)
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="parentOne")
  private List<Child> children;

  public void addChild(final Child child) {
    if (children == null) {
      children = new ArrayList<>();
    }

    children.add(child);

    child.setParentOne(this);
  }
}

以下几点值得注意:

  1. 模式名称(S1)已在实体实例必须保留的表名旁边声明。
  2. 与子实体的关系声明为集合,并注释为@OneToMany
  3. 持久性操作(保存,更新,删除)级联到子实体实例(cascade = CascadeType.ALL)。这可确保父实例上的持久性操作自动处理对任何关联子实例的适当操作。
  4.   

    ParentTwo 实体

    @Entity
    @Table(name="ParentTwo", schema="S2")
    public class ParentTwo {
      @Id
      @Column(name="ParentTwoID", unique=true, nullable=false)
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Integer id;
    
      @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="parentTwo")
      private List<Child> children;
    
      public void addChild(final Child child) {
        if (children == null) {
          children = new ArrayList<>();
        }
    
        children.add(child);
    
        child.setParentTwo(this);
      }
    }
    
      

    儿童实体

    @Entity
    @Table(name="ChildTable", schema="S2")
    public class Child {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name="ChildTableID", unique=true, nullable=false)
      private Integer id;
    
      @ManyToOne(cascade=CascadeType.PERSIST)
      @JoinColumn(name="ParentOneID", nullable=false)
      private ParentOne parentOne;
    
      @ManyToOne(cascade=CascadeType.PERSIST)
      @JoinColumn(name="ParentTwoID", nullable=false)
      private ParentTwo parentTwo;
    
      void setParentOne(final ParentOne parentOne) {
        this.parentOne = parentOne;
      }
    
      void setParentTwo(final ParentTwo parentTwo) {
        this.parentTwo = parentTwo;
      }
    }
    

    以下几点值得注意:

    1. 架构名称(S2)在表名旁边声明。
    2. 与父表的关联被声明为对象实例,而不是原始列。
    3. 在此之后,以下代码将正常工作:

      Child child = new Child();
      ...
      
      ParentOne parentOne = new ParentOne();
      ParentTwo parentTwo = new ParentTwo();
      ...
      
      parentOne.addChild(child);
      parentTwo.addChild(child);
      
      parentOneRepository.save(parentOne);