Если клиент существует в нашей базе данных (мы ищем по customId и accountId), мы проверяем, изменились ли поля (например, имя, возраст, телефон), и обновляем клиента. Если клиент не существует, мы просто создаем новый.
Проблема заключается в том, что рабочий поток создает новый клиент, а затем через несколько циклов ищет этот клиент (по customId и accountId) снова, но не находит его и пытается вставить нового клиента, что приводит к ConstraintViolationException. Мы не понимаем, почему это происходит, ведь даже при дублировании клиентов в массиве JSON рабочий поток должен был найти ранее вставленный клиент и соответствующим образом обновить его информацию.
Версия Dropwizard: 1.3.29
Версия MySQL: 8.0.31
Stacktrace:
Код: Выделить всё
java.lang.RuntimeException: org.hibernate.exception.ConstraintViolationException: could not execute statement
at br.com.system.db.ClientDao.transaction(ClientDao.java:95)
at br.com.system.db.ClientDao.insert(ClientDao.java:46)
at br.com.system.remotesystems.agent.Integration.loadTransactions(Integration.java:1355)
at br.com.system.core.system.integration.IntegrationJob.run(IntegrationJob.java:44)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:59)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:178)
at org.hibernate.dialect.identity.GetGeneratedKeysDelegate.executeAndExtract(GetGeneratedKeysDelegate.java:57)
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:42)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2933)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3524)
at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:637)
at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282)
at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263)
at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317)
at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:318)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:275)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:113)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:692)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:684)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:679)
at br.com.system.db.AbstractDao.lambda$insert$7(AbstractDao.java:242)
at br.com.system.db.AbstractDao.executeCall(AbstractDao.java:126)
... 13 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1399856-1699' for key 'Client.customId'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:118)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1061)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1009)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1320)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:994)
at sun.reflect.GeneratedMethodAccessor66.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114)
at com.sun.proxy.$Proxy124.executeUpdate(Unknown Source)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
... 36 more
Код: Выделить всё
public class Client {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int clientId;
private String customId; // external system id
private int accountId;
private String name;
... other fields like phone, birthdate, etc
}
Код: Выделить всё
Client client = clientDao.findByAccountIdAndCustomId(accountId, customId);
if (client == null) {
client = new Client(accountId, customId, name, ...other fields);
client = clientDao.insert(client);
} else /* client already exists */ {
boolean update = false;
if (!name.equals(client.getName())) {
update = true;
client.setName(name);
}
// ... checks and updates other fields
if (update)
clientDao.update(client);
}
Код: Выделить всё
public class ClientDao extends AbstractDao {
public ClientDao(SessionFactory session) {
super(session);
}
public Client insert(Client client) {
return transaction(() -> {
currentSession().save(client);
return client;
}, true);
}
public Client update(Client client) {
return transaction(() -> {
currentSession().update(client);
return client;
}, true);
}
protected E transaction(Callable call, boolean commit) {
if (ManagedSessionContext.hasBind(sessionFactory)) {
Session session = currentSession();
if (session.isOpen()) {
return call.call();
}
}
try (Session session = sessionFactory.openSession()) {
ManagedSessionContext.bind(session);
Transaction transaction = session.beginTransaction();
E result = call.call();
if (commit)
transaction.commit();
return result;
} finally {
ManagedSessionContext.unbind(sessionFactory);
}
}
public Client findByAccountIdAndCustomId(int accountId, String customId) {
return transaction(() -> {
}, false);
}
}
Код: Выделить всё
CREATE TABLE `Client` (
`clientId` int unsigned NOT NULL AUTO_INCREMENT,
`customId` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`name` varchar(115) COLLATE utf8mb4_unicode_ci NOT NULL,
`accountId` int unsigned DEFAULT NULL,
# ... other fields
PRIMARY KEY (`clientId`),
UNIQUE KEY `customId` (`customId`,`accountId`),
)
- Мы пытались использовать метод сохранения Dropwizard AbstractDAO вместо методов currentSession().save и currentSession().update, но это не помогло что-либо изменить.
- Мы попытались установить флаги базы данных Dropwizard commitOnReturn и autoCommitByDefault (как указано в https://www.dropwizard.io/en/stable/man ... l#database), но это не сработало.
Подробнее здесь: https://stackoverflow.com/questions/792 ... -commiting
Мобильная версия