【问题标题】:Constraint Violation when persisting One To Many relation坚持一对多关系时违反约束
【发布时间】:2014-10-16 18:16:33
【问题描述】:

在使用 hibernate 和 MySQL 的 spring mvc 应用程序中,我收到以下约束冲突异常:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:  
Cannot add or update a child row: a foreign key constraint fails  
(`mybd`.`hl7_documententity`, CONSTRAINT `hl7_documententity_ibfk_1`   
FOREIGN KEY (`ptcode`, `ptcodesystem`)    
REFERENCES `hl7_generalcode` (`code`, `codesystem`))

当我尝试保存包含GeneralCode 类型属性的DocumentEntity 时出现问题,这两个属性都在下面定义。

我已经阅读了很多关于这个错误的帖子和博客,但似乎没有一个能解决我的问题。 如何解决此错误?

这是DocumentEntity 类:

@Entity
@Table(name = "hl7_documententity")
public class HL7DocumentEntity extends BaseEntity{

    //other properties

    @ManyToOne
    @JoinColumns({ @JoinColumn(name = "ptcode", referencedColumnName = "code"),
        @JoinColumn(name = "ptcodesystem", referencedColumnName = "codesystem")
    })
    private HL7GeneralCode providertype;

    //getters and setters    
}

这是GeneralCode 类:

@Entity
@Table(name = "hl7_generalcodes")
public class HL7GeneralCode implements Serializable{

    private static final long serialVersionUID = -8620565054475096516L;

    @EmbeddedId
    private HL7EmbedCodePK codePk;

    @OneToMany(mappedBy = "providertype")
    private Set<HL7DocumentEntity> documententities;

    ////////////getters and setters    
}

这是来自控制器的代码:

HL7GeneralCode authcode = processGeneralCode(grandkid);
HL7GeneralCode testcode = this.clinicService.findGeneralCodeByPK(authcode.getCodePk().getCode(), authcode.getCodePk().getCodesystem());
if(testcode==null){
    authcode.addDocumententity(mydent);
    this.clinicService.savehl7GeneralCode(authcode);
    mydent.setProvidertype(authcode);
    //this next line throws the error
    this.clinicService.savehl7DocumentEntity(mydent);
}else{
    //other stuff
}

这里是dao方法:

@Repository
public class JpaSomethingRepositoryImpl implements SomethingRepository {

    @PersistenceContext
    private EntityManager em;

    @Override
    @Transactional
    public void savehl7DocumentEntity(HL7DocumentEntity de) {
        HL7GeneralCode code = de.getProvidertype();
        if(code !=null && code.getCodePk()==null){//HL7GeneralCode is not persistent. We don't support that
            throw new IllegalStateException("Cannot persist an adress using a non persistent HL7GeneralCode");
        }
        System.out.println("=========================== inside jpaCdaRespository.saveDocEntity(de)");
        de.setProvidertype(null);
        if(code.getDocumententities()!=null){
            ArrayList<HL7DocumentEntity> addrList = new ArrayList<HL7DocumentEntity>();
            addrList.addAll(code.getDocumententities());
            addrList.remove(de);
            Set<HL7DocumentEntity> myaddrs = new HashSet<HL7DocumentEntity>(addrList);
            code.setDocumententities(myaddrs);
        }
        code = em.merge(code); 
        de.setProvidertype(code);
        code.addDocumententity(de);

        if (de.getId() == null) {
            System.out.println("[[[[[[[[[[[[ about to persist de ]]]]]]]]]]]]]]]]]]]]");
            em.persist(de);
        } else {
            System.out.println("]]]]]]]]]]]]]]]]]] about to merge de [[[[[[[[[[[[[[[[[[[[[");
            de = em.merge(de);
        }
    }
}

执行的 SQL 语句和 hibernate 试图通过 sql 插入的实际值是:

[[[[[[[[[[[[ about to persist de ]]]]]]]]]]]]]]]]]]]]
DEBUG SQL - insert into hl7_documententity (author_id, authpar_id, entitytype, id_extension, id_root, ptcode, ptcodesystem, id) values (?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into hl7_documententity (author_id, authpar_id, entitytype, id_extension, id_root, ptcode, ptcodesystem, id) values (?, ?, ?, ?, ?, ?, ?, ?)
TRACE BasicBinder - binding parameter [1] as [INTEGER] - <null>
TRACE BasicBinder - binding parameter [2] as [INTEGER] - <null>
TRACE BasicBinder - binding parameter [3] as [VARCHAR] - <null>
TRACE BasicBinder - binding parameter [4] as [VARCHAR] - NI
TRACE BasicBinder - binding parameter [5] as [VARCHAR] - nullFlavor
TRACE BasicBinder - binding parameter [6] as [VARCHAR] - UNK
TRACE BasicBinder - binding parameter [7] as [VARCHAR] - HL7NullFlavor
TRACE BasicBinder - binding parameter [8] as [INTEGER] - 32787
WARN  SqlExceptionHelper - SQL Error: 1452, SQLState: 23000
ERROR SqlExceptionHelper - Cannot add or update a child row: a foreign key constraint fails (`docbd`.`hl7_documententity`, CONSTRAINT `hl7_documententity_ibfk_1` FOREIGN KEY (`ptcode`, `ptcodesystem`) REFERENCES `hl7_generalcode` (`code`, `codesystem`))
INFO  AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements
WARN  warn - Handler execution resulted in exception

您可以阅读 EmbedCodePK 类代码by clicking on this link
您可以阅读整个堆栈跟踪 by clicking on this link.
Here is a link 到 BaseEntity 类的代码。

【问题讨论】:

  • 我看到了那个错误,但是在尝试删除/更新时。让我解释一下...如果我有一个名为Person 的类并且与Job 存在一对多关系并且存在Person 的数据库条目并且它引用Job,如果我尝试删除它Person 在删除其所有 Jobs 之前,我会产生该错误。我没有时间查看您的代码,但我认为相反,当子元素引用该父元素时,您正在更新父元素。哦,我刚刚看了你的代码,没有看到主键?您应该使用@Id 创建一个
  • 好吧,我看到了您的 @EmbeddedId,我认为这与此有关,如果您在将引用设置为其旧值时更改它,则会发生该错误
  • 我找不到任何解释。您需要调试 Hibernate 代码。
  • 我认为您应该在 em.persist(de) 之前放置另一个断点并查看您的 de 对象,因为它违反了您的数据库约束。
  • 如果你在持久化一对多关系时违反了约束,你就不能少持久化一个吗?

标签: java mysql spring hibernate jpa


【解决方案1】:

改变这个:

@OneToMany(mappedBy = "providertype")
private Set<HL7DocumentEntity> documententities;

到这里:

@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name = "Link_Documents", joinColumns = {@JoinColumn(name = "codePk", unique = true)}, inverseJoinColumns = {@JoinColumn(name = "change_this_with_primary_key_variable_name_from_HL7DocumentEntity")})
 private Set<HL7DocumentEntity> documententities;

并且在 HL7DocumentEntity 中更改如下:

这个

@ManyToOne
    @JoinColumns({ @JoinColumn(name = "ptcode", referencedColumnName = "code"),
        @JoinColumn(name = "ptcodesystem", referencedColumnName = "codesystem")
    })
    private HL7GeneralCode providertype;

改成这样:

@ManyToOne(fetch = FetchType.LAZY)
  @JoinTable(name = "Link_Documents", joinColumns = {@JoinColumn(name = "change_this_with_primary_key_variable_name_from_HL7DocumentEntity")}, inverseJoinColumns = {@JoinColumn(name = "codePk")})
  private HL7GeneralCode providertype;

我认为您必须像在 BaseEntity 中一样将“change_this_with_primary_key_variable_name_from_HL7DocumentEntity”更改为“id”,但请查看您的 sql 表,您会看到正确的名称。

我希望您注意到我如何告诉 JPA 使用相同的“Link_Documents”表来链接 2 个表。我认为这是你的错误。只要确保用正确的变量名更改我告诉你的位置,我认为它应该可以工作

【讨论】:

【解决方案2】:

你知道我会从清理这段代码开始。

虽然休眠文档可能会告诉您将这些注释放在字段变量上是可以的,但作为标准,这是一个坏主意。字段级别需要休眠创建代理才能在延迟获取的情况下访问字段。所以只要习惯正确地做,你就不会不幸发现问题。将所有这些注释移动到 getter,因为属性级访问不需要代理。

字段级变量应该是私有的。

你在那里将一个整数转换为一个整数......其中一些东西让我不想看它,说实话可能会是一个愚蠢的懒惰,最终成为你的问题。

当你这样做时,用 new HashSet(0) 初始化集合;像这样的东西。仍然坏了更新我们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    相关资源
    最近更新 更多