Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring和Hibernate存在重复的关键问题-需要帮助_Java_Hibernate_Spring_Thread Safety_Duplicates - Fatal编程技术网

Java Spring和Hibernate存在重复的关键问题-需要帮助

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方法。我

-摘要缩短-

我有一个控制器,它从相应的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>