【问题标题】:Persist Tables with parent-child relation between two different schema in Spring Data JPA在 Spring Data JPA 中的两个不同模式之间保持具有父子关系的表
【发布时间】:2017-05-01 09:21:17
【问题描述】:

我正在努力,

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

其中,ParentOne 位于 schema S1 中,ParentTwo 以及 ChildTable 位于 schema S2 中。

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。我将只保留 ParentOne 以及不同模式的 ChildTable。当我这样做时,我会收到错误提示。

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

原因:com.microsoft.sqlserver.jdbc.SQLServerException: 不能 将值 NULL 插入列“ParentOneID”,表 'PT_Sample.S2.ChildTable';列不允许空值。插入失败。

SQL 错误:515,SQLState:23000

【问题讨论】:

  • 该错误表明ChildTable.ParentOneID 列具有NOT NULL 约束,并且正在发出INSERT,该列具有null 值。由于您没有在代码中的任何位置设置该列的值,因此预计会出现错误。究竟是什么问题?
  • @manish 因为 JPA 将插入 ParentOne 表,该表将创建一个 parentOneID,然后使用生成的 parentOneID 插入 ChildTable。但对我来说,这并没有发生。

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


【解决方案1】:

使用 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)。这可确保父实例上的持久性操作自动处理任何关联子实例上的适当操作。

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. 与父表的关联被声明为对象实例,而不是原始列。

之后,下面的代码就可以正常工作了:

Child child = new Child();
...

ParentOne parentOne = new ParentOne();
ParentTwo parentTwo = new ParentTwo();
...

parentOne.addChild(child);
parentTwo.addChild(child);

parentOneRepository.save(parentOne);

【讨论】:

  • 是的,它可以工作,我想知道是否可以将所有三个表保存在一起(即)clild.setParentTwoID(prentTwoID); 我不打算给出这行代码它会工作吗?
  • 是的,只要将操作配置为级联到适当的相关实体,就可以使用任何实体实例启动保存,并将级联到所有其他实体。有关详细信息,请参阅编辑后的答案。
  • @manish 嗨!如果我在 hibernate.cfg.xml 上的 url 被定义为某个模式怎么办? stackoverflow.com/questions/56050784/…
  • @manish 就我而言,例如,模式 S2 会有所不同。但是 Schema S1 对所有其他 Schema 都是通用的。所以我不能向我的实体声明 S2。我只能声明 S1。而且,S2 架构将位于来自另一个来源的 URL...
  • @KenobiShan,在这种情况下需要两个DataSources。映射到 S1 的实体需要与使用常规 DataSourceEntityManager 关联,而未映射到 S1 的实体将需要与另一个 @ 关联987654334@ 使用不同的 DataSource 扩展 AbstractRoutingDataSourceAbstractRoutingDataSource 必须根据运行时信息指示要使用的架构。
猜你喜欢
  • 1970-01-01
  • 2020-09-25
  • 1970-01-01
  • 2020-08-20
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
  • 2021-06-04
  • 2017-03-15
相关资源
最近更新 更多