2014-10-07 148 views
0

最近我遇到了一个web应用程序的问题。我使用spring mvc restful应用程序和hibernate一起使用jpa。spring mvc hibernate restful(concurrency)问题

客户端可以使用这个格式建立一个XML文件:

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

网络应用程序将随后registerSCCF标签内的每一个数据映射到一个类,并将其保存在数据库中。

现在我的问题的痛苦,当我使用的soapUI和多线程测试测试它,我总是得到异常

[错误]断言失败发生(这可能表明在Hibernate中的一个bug,但更可能是由于不安全使用会话) org.hibernate.AssertionFailure:在draft.persistence.entity.dcrm.CustomersNoneSSO进入空ID(发生异常后不要冲洗会议)

造成的: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); 
} 

如果我设置所有的设置器,以静态值,如:

cns.setContact_email("[email protected]"); 

,而不是使用从该参数的值,则该应用多线程测试运行良好。

有控制器调用服务方法:

@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); 
} 

... 
} 

应该有什么控制器方法或服务方法都是错误的。仍然不知道什么是错的。

+0

首先,我会希望服务保存一切或非,我不会在控制器中循环,但那是我。看起来你的dao发布代码时有问题。 – 2014-10-07 14:06:37

+0

这是循环应该发生在控制器中的一个好处。但是他们确实全部保存在数据库中,除了少数几个发生错误时。无论如何,我会发布道。 – fe3o4 2014-10-07 14:08:41

+1

还有你的问题......永远不要*从不**(从不提及**)会话存储在一个成员变量中......'Session'不是线程安全的,你有一个实例现在想象一下多线程会发生什么......删除存储(和setter!)并始终使用'sessionFactory.getCurrentSession()'。 – 2014-10-07 14:14:04

回答

1

您的道路有缺陷。

你的道路是一个单身人士,只有一个。 Hibernate Session对象不是线程安全的,不应该跨线程使用。

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

正如我在评论中提到的,不要将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,如果实体被注释了,它应该很容易做到)。

+0

你真是太棒了,我刚刚编辑了getSession()方法,它非常完美!我将在后面的Spring Data JPA中深入探讨。 – fe3o4 2014-10-07 14:40:15