【问题标题】:spring mvc hibernate restful (concurrency) issuespring mvc hibernate restful(并发)问题
【发布时间】:2014-12-01 23:28:55
【问题描述】:

最近我遇到了 Web 应用程序的问题。我正在使用 spring mvc restful 应用程序和 hibernate 作为 jpa。

客户端可以使用这种格式构建一个xml文件:

<SCCF>
    <registerSCCF>...</registerSCCF>
    ...
    <registerSCCF>...</registerSCCF>
</SCCF>

然后,Web 应用程序会将 registerSCCF 标记内的每个数据映射到一个类并将其保存在数据库中。

现在我遇到的问题是,当我使用soapui和多线程测试对其进行测试时,我总是遇到异常

[ERROR] 发生断言失败(这可能表明 Hibernate 中存在错误,但更有可能是由于会话使用不安全) org.hibernate.AssertionFailure:draft.persistence.entity.dcrm.CustomersNoneSSO 条目中的空 id(发生异常后不要刷新 Session)

原因:org.hibernate.HibernateException:级联期间刷新很危险

org.hibernate.SessionException:会话已关闭!

这里是服务层代码:

@Transactional("dcrm")
public boolean postSCCFService(SCCFVO sccf){
    CustomersNoneSSO cns = new CustomersNoneSSO();
    cns.setAppid(sccf.getAppid());
    cns.setCustomer_name(sccf.getCustomer_name());
    cns.setCustomer_gender(sccf.getCustomer_gender());
    cns.setContact_mobile(sccf.getContact_mobile());
    cns.setContact_email(sccf.getContact_email());
    cns.setAddress_province(sccf.getAddress_province());
    cns.setAddress_city(sccf.getAddress_city());
    cns.setCustomer_address(sccf.getCustomer_address());
    cns.setCustomer_occupation(sccf.getCustomer_occupation());
    cns.setPurchase_brand(sccf.getPurchase_brand());
    cns.setPurchase_model(sccf.getPurchase_model());
    cns.setPurchase_date(sccf.getPurchase_date());
    cns.setPurchase_budget(sccf.getPurchase_budget());
    cns.setOwncar_selected(sccf.getOwncar_selected());
    cns.setOwncar_model(sccf.getOwncar_model());
    cns.setTestdrive_permission(sccf.getTestdrive_permission());
    cns.setMarketing_permission(sccf.getMarketing_permission());
    Timestamp t = new Timestamp(new Date().getTime());
    cns.setInsert_timestamp(t);
    cns.setUpdate_timestamp(t);
    cnsDao.makePersistent(cns);
}

如果我将所有 setter 设置为静态值,例如:

cns.setContact_email("test@test.test");

而不是使用参数中的值,然后应用程序在多线程测试中运行良好。

有控制器调用服务方法:

@RequestMapping(value = "/test",method=RequestMethod.POST)
public @ResponseBody SCCFResponseList getPostResults(@RequestBody SCCFVOList sccf){
    ...
    for(SCCFVO sccfvo : sccf.getSCCFVOList()){
        ...
        boolean result = sccfservice.postSCCFService(sccfvo);
        ...
    }
    ...
}

public class SCCFVOList {

这里是请求正文类:

@XmlElement(name="registerSCCF")
public class SCCFVOList {
private Vector<SCCFVO> SCCFVOList = null;

public Vector<SCCFVO> getSCCFVOList(){
    return SCCFVOList;
}

public void setSCCFVOList(Vector<SCCFVO> SCCFVOList){
    this.SCCFVOList = SCCFVOList;
}

}

这里是道

public class CNSDao extends GenericHibernateDAO<CustomersNoneSSO, Long> {}

public abstract class GenericHibernateDAO<T, ID extends Serializable>
    implements GenericDAO<T, ID> {

private Class<T> persistentClass;
private Session session;

SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory sessionFactory){
    this.sessionFactory = sessionFactory;
}

public GenericHibernateDAO() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0];
}

@SuppressWarnings("unchecked")
public void setSession(Session s) {
    this.session = s;
}

protected Session getSession() {
    session = sessionFactory.getCurrentSession();
    if (session == null)
        throw new IllegalStateException(
                "Session has not been set on DAO before usage");
    return session;
}

public Class<T> getPersistentClass() {
    return persistentClass;
}

@SuppressWarnings("unchecked")
public T makePersistent(T entity) {
    getSession().saveOrUpdate(entity);
    return entity;
}

public void makeTransient(T entity) {
    getSession().delete(entity);
}

...
}

控制器方法或服务方法应该有问题。仍然不知道出了什么问题。

【问题讨论】:

  • 首先,我希望该服务可以保存或不保存所有内容,我不会在控制器中循环,但这就是我。看起来你的 dao 发布代码有问题。
  • 这是一个很好的点,循环应该发生在控制器中。但它们确实都保存在数据库中,除了少数发生时。无论如何,我会发布 dao。
  • 还有你的问题......永远不要NEVER NEVER(我是否提到过)将会话存储在成员变量中...... Session 不是线程安全的,你只有一个 dao 实例,现在想象一下多线程会发生什么......删除存储(和设置器!)并始终使用 sessionFactory.getCurrentSession()
  • 我有点惊讶,因为我在这里使用了通用 dao 模式developer.jboss.org/wiki/GenericDataAccessObjects 不知何故我将会话变量的私有更改为受保护。设置回来后仍然有同样的异常:(你的意思是我应该在 CNSDao 类中使用会话吗?
  • 正如我提到的,永远不要使用实例变量来存储会话!删除属性,删除 setter,getter 应该只是对sessionFactory.getCurrentSession() 的调用。 100 个线程都试图在 dao 的单个实例上设置和使用会话,你会怎么做?这只有在 dao 不是单例时才有效。

标签: javascript multithreading spring hibernate spring-mvc


【解决方案1】:

你的道有缺陷。

你的 dao 是一个单例,只有一个。 Hibernate Session 对象不是线程安全的,不应跨线程使用。

您有 1 个 dao,2 个线程,线程 1 获取会话的实例 X1,线程 2 将其重置为实例 X2 现在突然它们共享同一个会话,更不用说线程 1 甚至可能在 2 个不同的会话上运行。

正如我在 cmets 中提到的,切勿将 Session 存储在实例变量中。删除它。

public abstract class GenericHibernateDAO<T, ID extends Serializable> implements GenericDAO<T, ID> {

    private Class<T> persistentClass;

    private SessionFactory sessionFactory;

    public GenericHibernateDAO() {
        this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public void setSessionFactory(SessionFactory sessionFactory){
        this.sessionFactory = sessionFactory;
    }


    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }

另外我建议放弃这个,而使用Spring Data JPA 可以省去创建和维护自己的通用 dao 的麻烦。 (你提到你使用 JPA,如果实体被注释它应该很容易做到)。

【讨论】:

  • 你太棒了。我刚刚按照您的建议编辑了 getSession() 方法。它很完美!稍后我将深入研究 Spring Data JPA。大声笑
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 2013-04-09
  • 2016-11-10
  • 1970-01-01
  • 1970-01-01
  • 2012-02-02
相关资源
最近更新 更多