Основная проблема здесь в том, что отношения двунаправлены, и обе стороны изменены и версируются. Когда вы вызываете addToAttempts
, коллекция attempts
, сгенерированная свойством hasMany
, инициализируется новой пустой коллекцией, если она равна null, затем экземпляр добавляется к ней, а в поле Student экземпляра устанавливается владеющий учащийся, чтобы убедиться, что в памяти состояние такое же, как и позднее, если вы перезагрузите все из базы данных. Но когда вы включили управление версиями (оптимистичная блокировка), так как обе стороны изменились, оба получат ошибку. Поэтому, если вы перекрываете коллекцию между двумя одновременными пользователями, вы получаете эту ошибку. И это реально - вы рискуете потерять предыдущее обновление, если вы явно не блокируете или не используете оптимистичную блокировку.
Но все это полностью искусственно. Это похоже на много-ко-многим, поэтому все, что вам нужно, - это добавить новую строку в таблицу соединений, которая указывает ученику и попытку. Grails делает это, настраивая коллекции, которые Hibernate обнаруживает изменения, но это действительно использует побочный эффект. Это также очень дорого для больших коллекций. Я оставил одну часть выше о вызове addToAttempts
; если бы там уже были экземпляры, каждый из них будет извлекаться из базы данных, хотя вам не нужно ни одного из них. Grails загружает все N предыдущих элементов (где N может быть очень большим числом) и добавляет новый N + 1 st, поэтому Hibernate может обнаружить этот новый элемент. Все, что вам нужно, это вставить одну строку, и вы получите значительное количество трафика базы данных.
Исправление состоит в том, чтобы не разбрасывать merge
и withTransaction
звонки или другие случайные материалы, которые вы найдете здесь или в другом месте - это удаление одновременного доступа. Здесь вам повезло, потому что это абсолютно искусственно. См. Этот разговор, который я сделал некоторое время назад, что, к сожалению, так же актуально, как и в нынешних Grails, как это было тогда - я описываю подходы к удалению коллекций и замену их более разумными подходами: http://www.infoq.com/presentations/GORM-Performance
Я был в подобных ситуациях, что происходит если вы вызываете .merge() на studentQuizInstance, прежде чем вызывать его на studentInstance? – marko
Есть пара аккуратных отладочных трюков для подобных ситуаций (думаю, это помогло мне решить аналогичную проблему в тот же день) - http://stackoverflow.com/questions/536601/what-are-your-favorite-grails-debugging -tricks – marko