为什么Hibernate在没有插入子行的情况下插入带有外键的父行?

时间:2011-06-27 14:14:04

标签: java mysql hibernate orm

我希望有人之前遇到过这个问题,可以帮助我。基本上,Hibernate插入父行(ID指向子行),但不插入具有关联ID的子行,这使数据库处于错误状态。这是Hibernate尝试加载未正确保存的对象时抛出异常的示例:

27 Jun 2011 13:55:31,380 ERROR [scheduler_Worker-4] - 
Job DEFAULT.queryScrubJobDetail threw an unhandled Exception: 
org.springframework.scheduling.quartz.JobMethodInvocationFailedException: 
Invocation of method 'doIt' on target class [XXX] failed; nested exception is
org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException: 
No row with the given identifier exists: 
[XXX.DataProviderTransaction#60739703]; nested exception is org.hibernate.ObjectNotFoundException: 
No row with the given identifier exists: 
[com.idology.persist.DataProviderTransaction#2]

该应用程序的这一部分有三个实体:

  • 查询,它是DataProviderTransactionReference和DataProviderTransaction的父级
  • DataProviderTransaction ,它是Query的子项,也是DataProviderTransactionReference的父项
  • DataProviderTransactionReference ,其外键指向DataProviderTransaction和Query

以下是映射:

来自查询

@OneToMany(mappedBy = "query", cascade =
    { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY)
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
@JoinColumn(name = "query_id")
public List<DataProviderTransactionReference> getDataProviderTransactionReferences()

来自 DataProviderTransaction

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "query_id")
public Query getQuery()

来自 DataProviderTransactionReference

@ManyToOne(cascade =
    { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER)
@JoinColumn(name = "data_provider_transaction_id")
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
public DataProviderTransaction getDataProviderTransaction()
{
    return mDataProviderTransaction;
}

架构看起来像这样(省略查询表,因为它没有外键):

data_provider_transaction

+------------------+---------------+------+-----+---------+----------------+
| Field            | Type          | Null | Key | Default | Extra          |
+------------------+---------------+------+-----+---------+----------------+
| id               | bigint(20)    | NO   | PRI | NULL    | auto_increment |
| query_id         | bigint(20)    | YES  | MUL | NULL    |                |
+------------------+---------------+------+-----+---------+----------------+

data_provider_txn_refs

+------------------------------+------------+------+-----+---------+----------------+
| Field                        | Type       | Null | Key | Default | Extra          |
+------------------------------+------------+------+-----+---------+----------------+
| id                           | bigint(20) | NO   | PRI | NULL    | auto_increment |
| created_at                   | datetime   | YES  |     | NULL    |                |
| data_provider_transaction_id | bigint(20) | YES  | MUL | NULL    |                |
| query_id                     | bigint(20) | YES  | MUL | NULL    |                |
+------------------------------+------------+------+-----+---------+----------------+

因此,一旦我们完成了运行查询(由Query对象表示),我们使用Spring和Hibernate使用以下命令保存它:

getHibernateTemplate().saveOrUpdate(aQuery);

Query与相关的DataProviderTransaction和DataProviderTransactionReference实体一起保存。除此之外,它有时会保存Query和DataProviderTransactionReference,而不会保存关联的DataProviderTransaction。它确实在data_provider_transaction_id中放置了一个ID,但它指向了data_provider_transaction表中不存在的行。

下一步是添加一个外键约束,以便在我们进行初始保存时发生问题,而不是在我们稍后尝试加载对象时发生。

我们使用的是Spring 2.5.6,Hibernate 3.3.2和MySQL 5.0。我已经看到多年来早期版本的Spring和Hibernate出现了这个问题。

有人见过/解决了这个问题吗?

1 个答案:

答案 0 :(得分:1)

这听起来像是你的MySQL id分配问题。如果未正确声明生成器,或者您使用代码执行奇怪的操作,Hibernate可能会感到困惑。

您是否拥有孤立的DataProviderTransactions或DataProviderTransactionReferences,一个DataProviderTransaction,其查询ID不存在,或指向错误的查询?

您的发电机是否被声明为您的ID的身份? (参见Chapter 5. Basic O/R Mapping, section 5.1.4 id。通常情况下,这应该足够了,但是你可能还有其他一些事情会混淆休眠。

所以,例如:

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)

要真正跟踪此情况,您需要才能知道发生这种情况的原因,谁正在插入这些行。您需要数据库中的外键约束。有些东西也可能正在删除DataProviderTransaction,并且数据库没有抱怨,因为没有外键。