我正在一个数据库应用程序上工作,该应用程序大部分是只读的,但是有一个表记录应用程序中用户的移动并对其进行大量写入。对于每几千次写入,我们在错误日志中看到一些异常,如下所示:
[WARN][2009-07-30 11:09:20,083][org.hibernate.util.JDBCExceptionReporter] SQL Error: 1062, SQLState: 23000 [ERROR][2009-07-30 11:09:20,083][org.hibernate.util.JDBCExceptionReporter] Duplicate entry '17011' for key 1 [ERROR][2009-07-30 11:09:20,083][org.hibernate.event.def.AbstractFlushingEventListener] Could not synchronize database state with session org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
有问题的表具有以下架构:
CREATE TABLE IF NOT EXISTS `my_table` ( `id` int(11) NOT NULL, `data1` int(11) NOT NULL, `data2` int(11) NOT NULL, `timestamp` datetime default NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
以及对应的Hibernate映射XML:
<hibernate-mapping> <class name="mycorp.MyClass" table="my_table"> <id name="id" column="id" type="java.lang.Integer"> <generator class="increment"/> </id> <property name="data1" column="data1" type="java.lang.Integer"/> <property name="data2" column="data2" type="java.lang.Integer"/> <property name="timestamp" column="timestamp" type="java.util.Date"/> </class> </hibernate-mapping>
尽管我们不太可能,但我们的web应用程序的多个实例可能一次写入数据库,因为我们在webapp上下文中对版本号进行了编码,以无缝发布应用程序的新版本。因此,使用旧版本应用程序缓存在其Web浏览器中的客户端将访问服务器的旧版本,我们将在数周后取消部署该服务器。
无论如何,我不相信这是问题所在,但是我怀疑这里的MySQL和Hibernate之间存在一些同步问题。将生成器更改为sequence,seqhilo或hilo会有所帮助吗?另外,如果您可以提供在MySQL中设置此类生成器的示例,那将非常有帮助,因为大多数在线资源都只是从Hibernate手册中极简主义的示例中复制粘贴而来。
如果您有多个进程在同一张表中写入数据,那么增量肯定是不好的-势必会发生冲突。
由于我们正在谈论的是MySQL,因此最容易使用的是identity。在您的Hibernate映射中:
identity
<generator class="identity"/>
在您的MySQL脚本中:
CREATE TABLE IF NOT EXISTS `my_table` ( `id` int(11) NOT NULL AUTO_INCREMENT, `data1` int(11) NOT NULL, `data2` int(11) NOT NULL, `timestamp` datetime default NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
更改现有表:
ALTER TABLE `my_table` CHANGE COLUMN `id` `id` int(11) NOT NULL AUTO_INCREMENT=$NEW_VALUE$;
其中$ NEW_VALUE $应该用下一个可用ID替换,这样顺序就不会重置为1。