Java Spring和Hibernate存在重复的关键问题-需要帮助
-摘要缩短- 我有一个控制器,它从相应的DAO加载一个概要文件对象。它更新一些属性,其中许多属性集,然后通过DAO中的save调用saveOrUpdate以重新附加和更新概要文件对象。在看似随机的时间间隔内,我们会得到一个org.hibernate.exception.ConstraintViolationException,其根本原因是:java.sql.BatchUpdateException:键1的重复条目“3-56”。堆栈跟踪指向从概要文件更新控制器调用的saveOrUpdate方法。我不能在我的测试环境中复制,我们只能在生产环境中看到这一点,所以我想知道我是否遗漏了一些与线程安全相关的内容,这就是为什么我要发布这么多代码/配置信息的原因。有什么想法吗 -代码- 我已尝试提供尽可能多的相关配置/代码-如果需要更多,请告诉我: 以下是违规控制员的摘录:Java Spring和Hibernate存在重复的关键问题-需要帮助,java,hibernate,spring,thread-safety,duplicates,Java,Hibernate,Spring,Thread Safety,Duplicates,-摘要缩短- 我有一个控制器,它从相应的DAO加载一个概要文件对象。它更新一些属性,其中许多属性集,然后通过DAO中的save调用saveOrUpdate以重新附加和更新概要文件对象。在看似随机的时间间隔内,我们会得到一个org.hibernate.exception.ConstraintViolationException,其根本原因是:java.sql.BatchUpdateException:键1的重复条目“3-56”。堆栈跟踪指向从概要文件更新控制器调用的saveOrUpdate方法。我
public class EditProfileController extends SimpleFormController {
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception
{
if(!checkLoggedIn(request))
{
return new ModelAndView("redirect:" + invalidRedirect);
}
HttpSession session = request.getSession();
Resource resource = (Resource)session.getAttribute("resource"); //The resource object is stored in session upon login and upon account creation.
Profile profile = profiles.getProfileByResource(resource);
if(profile == null)
{
profile = new Profile();
profile.setResource(resource);
}
//I use custom editors to populate the sets in the command object with objects based on the selection
if(profile.getPrimaryRoleSkills() != null && editProfileCommand.getPrimaryRoleSkills() != null)
{
profile.getPrimaryRoleSkills().addAll(editProfileCommand.getPrimaryRoleSkills());
profile.getPrimaryRoleSkills().retainAll(editProfileCommand.getPrimaryRoleSkills());
}
else
profile.setPrimaryRoleSkills(editProfileCommand.getPrimaryRoleSkills());
profiles.save(profile); //This is the line that appears in the stack trace
return new ModelAndView(getSuccessView());
}
//Other methods omitted
}
缩略配置文件类:
public class Profile implements java.io.Serializable {
private long id;
private Resource resource;
private Set<PrimaryRoleSkill> primaryRoleSkills = new HashSet<PrimaryRoleSkill>(0);
public Profile() {
}
//Other properties trivial or similar to above. Getters and setters omitted
//toString, equals, and hashCode are all generated by hbm2java
}
这是我的DAO基类:
public class DAO {
protected DAO() {
}
public static Session getSession() {
Session session = (Session) DAO.session.get();
if (session == null) {
session = sessionFactory.openSession();
DAO.session.set(session);
}
return session;
}
protected void begin() {
getSession().beginTransaction();
}
protected void commit() {
getSession().getTransaction().commit();
}
protected void rollback() {
try {
getSession().getTransaction().rollback();
} catch( HibernateException e ) {
log.log(Level.WARNING,"Cannot rollback",e);
}
try {
getSession().close();
} catch( HibernateException e ) {
log.log(Level.WARNING,"Cannot close",e);
}
DAO.session.set(null);
}
public boolean save(Object object)
{
try {
begin();
getSession().saveOrUpdate(object);
commit();
return true;
}
catch (HibernateException e) {
log.log(Level.WARNING,"Cannot save",e);
rollback();
return false;
}
}
private static final ThreadLocal<Session> session = new ThreadLocal<Session>();
private static final SessionFactory sessionFactory = new Configuration()
.configure().buildSessionFactory();
private static final Logger log = Logger.getAnonymousLogger();
//Non-related methods omitted.
}
相关弹簧配置:
<bean id="profiles" class="com.xxxx.dao.Profiles" />
<bean id="editProfileController" class="com.xxxx.controllers.EditProfileController">
<property name="sessionForm" value="false" />
<property name="commandName" value="editProfileCommand" />
<property name="commandClass" value="com.xxxx.commands.EditProfileCommand" />
<property name="profiles" ref="profiles" />
<property name="formView" value="EditProfile" />
<property name="successView" value="redirect:/profile" />
<property name="validator" ref="profileValidator" />
</bean>
hibernate.cfg.xml
<session-factory>
<property name="connection.driver_class">@driver@</property>
<property name="connection.url">@connectionurl@</property>
<property name="connection.username">@dbuser@</property>
<property name="connection.password">@dbpw@</property>
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="dbcp.maxActive">15</property>
<property name="dbcp.maxIdle">5</property>
<property name="dbcp.maxWait">120000</property>
<property name="dbcp.whenExhaustedAction">2</property>
<property name="dbcp.testOnBorrow">true</property>
<property name="dbcp.testOnReturn">true</property>
<property name="dbcp.validationQuery">
select 1
</property>
<property name="dbcp.ps.maxActive">0</property>
<property name="dbcp.ps.maxIdle">0</property>
<property name="dbcp.ps.maxWait">-1</property>
<property name="dbcp.ps.whenExhaustedAction">2</property>
<!-- Echo all executed SQL to stdout
<property name="show_sql">true</property>
-->
<mapping resource="com/xxxx/entity/Resource.hbm.xml"/>
<mapping resource="com/xxxx/entity/Authentication.hbm.xml"/>
<mapping resource="com/xxxx/entity/NameValuePairs.hbm.xml"/>
<mapping resource="com/xxxx/entity/Profile.hbm.xml"/>
<mapping resource="com/xxxx/entity/FileData.hbm.xml"/>
</session-factory>
摘录自Profile.hbm.xml:
<hibernate-mapping>
<class name="com.xxxx.entity.Profile" select-before-update="true">
<id name="id" type="long">
<generator class="foreign">
<param name="property">resource</param>
</generator>
</id>
<set name="primaryRoleSkills" cascade="none">
<key column="profile"/>
<many-to-many column="primary_role_skill" class="com.xxxx.entity.PrimaryRoleSkill"/>
</set>
</class>
</hibernate-mapping>
摘自NameValuePairs.hbm.xml:
<hibernate-mapping>
<class name="com.xxxx.entity.NameValuePairs" abstract="true">
<id name="id" type="long">
<generator class="native" />
</id>
<discriminator column="type" type="string" />
<property type="string" name="name" length="256">
<meta attribute="use-in-equals">true</meta>
</property>
<property type="boolean" name="active">
<meta attribute="default-value">true</meta>
</property>
<subclass name="com.xxxx.entity.PrimaryRoleSkill" discriminator-value="PrimaryRoleSkill" />
</class>
</hibernate-mapping>
该应用程序运行在Tomcat 6.0.14上,并连接到运行在Linux上的MySQL 5.0.89-community版本。我们使用的是Hibernate 3.3.2和Spring Framework 2.5.6。我没有立即的答案-可能是多线程情况,也可能是测试环境中没有使用正确的输入数据来触发问题
如果这是我的代码,我首先将比较相等、CuffReto和Hash码实现与数据库定义——如果这些方法比较数据库所要求的唯一性,Hibernate可能会考虑两个对象不同,即使它们在数据库中使用相同的密钥。
另一种方法是将日志记录添加到所有保存并检索这些对象的地方,包括STACKTROPE显然带有ON/OFF开关。或者,当您收到重复密钥错误时,查询数据库并记录其中已有的内容。无论哪种方式,您都希望找到“第一条”记录的来源。
经过10天的无异常处理后,我得出结论,我发现的解决方案是有效的 简短回答:我将DAO切换为使用HibernateTemplate,并使用SpringAOP处理事务。这涉及到大量重写,但这是值得的,因为解决方案现在可以按预期工作。此外,我无法在JSP视图中实现延迟加载,但这并不是什么大问题,因为我的对象非常小,我在Hibernate配置中禁用了属性的延迟加载说明:问题在于我获取Hibernate会话的方式。在最初的实现中,在应用程序启动时为扩展DAO基类的每个DAO创建一个Hibernate会话。这造成了两个问题。1 Hibernate会话本身不是线程安全的。这就是为什么在一个测试实例上用一个用户测试一切都很好,但在生产中却有不寻常的行为。2 MySQL喜欢在一段时间后关闭连接。由于会议持续开放,这导致OP中未报告管道破裂,我认为这是一个单独的问题。使用此修复程序,Spring现在可以管理会话创建/关闭,Spring AOP可以处理事务标记,SpringTemplate甚至可以处理大部分Hibernate访问。尝试只包含相关信息。太长了,无法阅读..您的DAO不应该启动/提交/管理事务-业务对象服务应该启动/提交/管理事务。我缩短了描述。我已经删除了大部分的类代码,留下了一个有问题的集合的示例。难道DAO没有管理与Spring和Hibernate交互方式相关的事务吗?感谢您的响应!我又看了一眼hbm2java生成的equals和hashcode方法。它只包括名称字段,这是业务密钥。我一定会查日志的。
<hibernate-mapping>
<class name="com.xxxx.entity.Profile" select-before-update="true">
<id name="id" type="long">
<generator class="foreign">
<param name="property">resource</param>
</generator>
</id>
<set name="primaryRoleSkills" cascade="none">
<key column="profile"/>
<many-to-many column="primary_role_skill" class="com.xxxx.entity.PrimaryRoleSkill"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.xxxx.entity.NameValuePairs" abstract="true">
<id name="id" type="long">
<generator class="native" />
</id>
<discriminator column="type" type="string" />
<property type="string" name="name" length="256">
<meta attribute="use-in-equals">true</meta>
</property>
<property type="boolean" name="active">
<meta attribute="default-value">true</meta>
</property>
<subclass name="com.xxxx.entity.PrimaryRoleSkill" discriminator-value="PrimaryRoleSkill" />
</class>
</hibernate-mapping>