Почему Hibernate вставляет родительскую строку с внешним ключом, не вставляя дочернюю строку? - PullRequest
7 голосов
/ 27 июня 2011

Я надеюсь, что кто-то сталкивался с этой проблемой раньше и может мне помочь.По сути, Hibernate вставляет родительскую строку (с идентификатором, указывающим на дочернюю строку), но не вставляет эту дочернюю строку с соответствующим идентификатором, что оставляет базу данных в плохом состоянии.Вот пример исключения, которое выдается, когда 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]

Эта часть приложения имеет три объекта:

  • Query , который является родительским для DataProviderTransactionReference и DataProviderTransaction
  • DataProviderTransaction , который является дочерним по отношению к 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()

From 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);

Запрос сохраняется вместе со связанными сущностями DataProviderTransaction и DataProviderTransactionReference.За исключением того, что иногда он сохраняет Query и DataProviderTransactionReference без связанной DataProviderTransaction.Он помещает идентификатор в data_provider_transaction_id, но указывает на строку, которой нет в таблице data_provider_transaction.

Следующим шагом является добавление ограничения внешнего ключа, чтобы вызвать возникновение проблемы при первоначальном выполнении.сохранить, а не когда мы попытаемся загрузить объект позже.

Мы используем Spring 2.5.6, Hibernate 3.3.2 и MySQL 5.0.Я видел, как эта проблема возникала на протяжении многих лет с более ранними версиями Spring и Hibernate.

Кто-нибудь когда-либо видел / решил эту проблему?

1 Ответ

1 голос
/ 12 июля 2011

Это звучит как проблема с вашим распределением идентификаторов в MySQL. Hibernate может запутаться, если генераторы не объявлены правильно, или если вы делаете странные вещи с вашим кодом.

Есть ли у вас потерянные данные DataProviderTransactions или DataProviderTransactionReferences, DataProviderTransaction с идентификатором запроса, который не существует, или указывает на неправильный запрос?

Ваши генераторы объявлены как идентификаторы для ваших идентификаторов? (см. Глава 5. Основы O / R Mapping, раздел 5.1.4 id . Обычно этого должно быть достаточно, но могут быть и другие вещи, которые запутывают спящий режим.

Так, например:

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

Чтобы действительно отследить это, вам нужно , чтобы знать, почему это происходит, кто вставляет эти строки. Вам нужно ограничение внешнего ключа в базе данных. Также возможно, что что-то удаляет DataProviderTransaction и база данных не жалуется, потому что нет внешнего ключа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...