Получение исключения "Неудачное ограничение UNIQUE" с использованием метода Ormlite createOrUpdate()
У меня есть несколько вызовов по всему моему приложению, где таблицы в моей базе данных обновляются с помощью вызовов .createOrUpdate()
Документация предлагает из этих звонков:
Это удобный метод для создания элемента в базе данных, если он не существует. Идентификатор извлекается из параметра данных, и в базе данных выполняется запрос по идентификатору. Если в базе данных есть строка с таким же идентификатором, то все столбцы в базе данных будут обновлены из полей в параметре данных. Если идентификатор равен нулю (или 0 или другому значению по умолчанию) или не существует в базе данных, тогда объект будет создан в базе данных. Это также означает, что для вашего элемента данных должно быть определено поле id.
Тогда я понимаю, что при вызове createOrUpdate() базовый код должен вызывать "insert", если в базе данных нет строки, и "update", если строка существует.
Кажется, что происходит то, что основной код вызывает "вставку", когда строка уже существует, вызывая исключение.
Кто-нибудь знает, почему это происходит или как этого избежать?
Исключение ниже:
java.lang.RuntimeException: java.sql.SQLException: Unable to run insert stmt on object
Feed(id=126275579_organisations-email_58284484, type=organisations-email, category=1, msg=<p>Body</p>, regarding=Bobby Castle, subject=Subject, createdTs=2016-01-04T09:59:27+00:00, completed=0, read=0, starred=0, archived=0, remoteAttachments=[], localAttachments=null, authorId=250008275, authorName=Live Regression Test, authorImg=null): INSERT INTO `feed` (`id` ,`type` ,`category` ,`msg` ,`regarding` ,`subject` ,`createdTs` ,`completed` ,`read` ,`starred` ,`archived` ,`authorId` ,`authorName` ,`authorImg` ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
at com.j256.ormlite.dao.RuntimeExceptionDao.createOrUpdate(RuntimeExceptionDao.java:252)
at uk.co.test.test.data.orm.models.Feed.createOrUpdate(Feed.java:81)
at uk.co.test.test.interactors.server.CategoryInteractor$6.onSuccessfulResponse(CategoryInteractor.java:308)
at uk.co.test.test.interactors.server.CategoryInteractor$6.onSuccessfulResponse(CategoryInteractor.java:290)
at uk.co.test.test.interactors.common.StandardCallbackRunnable.checkStandardResponse(StandardCallbackRunnable.java:27)
at uk.co.test.test.interactors.common.StandardCallbackRunnable.checkStandardResponse(StandardCallbackRunnable.java:12)
at uk.co.test.test.interactors.common.CallbackRunnable.success(CallbackRunnable.java:127)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.sql.SQLException: Unable to run insert stmt on object Feed(id=126275579_organisations-email_58284484, type=organisations-email, category=1, msg=<p>Body</p>, regarding=Bobby Castle, subject=Subject, createdTs=2016-01-04T09:59:27+00:00, completed=0, read=0, starred=0, archived=0, remoteAttachments=[], localAttachments=null, authorId=250008275, authorName=Live Regression Test, authorImg=null): INSERT INTO `feed` (`id` ,`type` ,`category` ,`msg` ,`regarding` ,`subject` ,`createdTs` ,`completed` ,`read` ,`starred` ,`archived` ,`authorId` ,`authorName` ,`authorImg` ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
at com.j256.ormlite.misc.SqlExceptionUtil.create(SqlExceptionUtil.java:22)
at com.j256.ormlite.stmt.mapped.MappedCreate.insert(MappedCreate.java:135)
at com.j256.ormlite.stmt.StatementExecutor.create(StatementExecutor.java:450)
at com.j256.ormlite.dao.BaseDaoImpl.create(BaseDaoImpl.java:310)
at com.j256.ormlite.dao.BaseDaoImpl.createOrUpdate(BaseDaoImpl.java:336)
at com.j256.ormlite.dao.RuntimeExceptionDao.createOrUpdate(RuntimeExceptionDao.java:249)
at uk.co.test.test.data.orm.models.Feed.createOrUpdate(Feed.java:81)
at uk.co.test.test.interactors.server.CategoryInteractor$6.onSuccessfulResponse(CategoryInteractor.java:308)
at uk.co.test.test.interactors.server.CategoryInteractor$6.onSuccessfulResponse(CategoryInteractor.java:290)
at uk.co.test.test.interactors.common.StandardCallbackRunnable.checkStandardResponse(StandardCallbackRunnable.java:27)
at uk.co.test.test.interactors.common.StandardCallbackRunnable.checkStandardResponse(StandardCallbackRunnable.java:12)
at uk.co.test.test.interactors.common.CallbackRunnable.success(CallbackRunnable.java:127)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.sql.SQLException: inserting to database failed: INSERT INTO `feed` (`id` ,`type` ,`category` ,`msg` ,`regarding` ,`subject` ,`createdTs` ,`completed` ,`read` ,`starred` ,`archived` ,`authorId` ,`authorName` ,`authorImg` ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
at com.j256.ormlite.misc.SqlExceptionUtil.create(SqlExceptionUtil.java:22)
at com.j256.ormlite.android.AndroidDatabaseConnection.insert(AndroidDatabaseConnection.java:169)
at com.j256.ormlite.stmt.mapped.MappedCreate.insert(MappedCreate.java:91)
at com.j256.ormlite.stmt.StatementExecutor.create(StatementExecutor.java:450)
at com.j256.ormlite.dao.BaseDaoImpl.create(BaseDaoImpl.java:310)
at com.j256.ormlite.dao.BaseDaoImpl.createOrUpdate(BaseDaoImpl.java:336)
at com.j256.ormlite.dao.RuntimeExceptionDao.createOrUpdate(RuntimeExceptionDao.java:249)
at uk.co.test.test.data.orm.models.Feed.createOrUpdate(Feed.java:81)
at uk.co.test.test.interactors.server.CategoryInteractor$6.onSuccessfulResponse(CategoryInteractor.java:308)
at uk.co.test.test.interactors.server.CategoryInteractor$6.onSuccessfulResponse(CategoryInteractor.java:290)
at uk.co.test.test.interactors.common.StandardCallbackRunnable.checkStandardResponse(StandardCallbackRunnable.java:27)
at uk.co.test.test.interactors.common.StandardCallbackRunnable.checkStandardResponse(StandardCallbackRunnable.java:12)
at uk.co.test.test.interactors.common.CallbackRunnable.success(CallbackRunnable.java:127)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: feed.id (code 1555)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
at android.
1 ответ
Тогда я понимаю, что при вызове createOrUpdate() базовый код должен вызывать "insert", если в базе данных нет строки, и "update", если строка существует.
Это верно, однако операция не является (и не может быть) атомарной, поскольку для ее выполнения выполняется несколько вызовов базы данных. В методе DAO нет блокировки, хотя, возможно, и должно быть.
Если мы посмотрим на BaseDaoImpl.createOrUpdate(...)
код вы можете увидеть, что это делает:
- Извлеките идентификатор из рассматриваемого объекта.
- Ищет его в базе данных, чтобы увидеть, существует ли он.
- Вызовы
update(...)
если существует иначеcreate()
,
Поскольку происходит несколько обращений к базе данных, существуют условия состязания, если несколько потоков одновременно выполняют этот метод.
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
Правильно, если вы используете этот метод DAO с потоками (и createIfNotExists(...)
который также делает 2 вызова базы данных), то я бы сделал это в течение synchronized
блок:
synchronized (dao) {
status = dao.createOrUpdate(...);
}