我用Spring 3.0.0.RC1做了一個非常簡單的REST控制器方法,它使用hibernate執行查詢。查詢需要大約十秒鐘才能完成。我已經做了這個意圖,以便我可以向控制器發出兩個請求。從兩個線程使用相同的服務和DAO
然後,我啓動了這兩個請求,並在MySQL(我的數據庫後端)「顯示完整進程列表」中查詢,令我大吃一驚的是,只有一個請求正在進行。一個請求會成功,一個請求會失敗,並會出現異常「org.hibernate.SessionException:Session is closed!」如果我做了兩個以上的請求,只有一個會成功,其他人會以同樣的方式失敗。一次只會有一個查詢,即使應該有多個查詢。
這怎麼可能?有什麼建議麼?
要告訴你一些關於我的配置,這裏是配置,我使用的控制器:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/MyDb" />
<property name="username" value="angua" />
<property name="password" value="vonU" />
<property name="initialSize" value="2" />
<property name="maxActive" value="5" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedClasses">
<list>
<value>tld.mydomain.sample.entities.User</value>
<value>tld.mydomain.sample.entities.Role</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="flushMode" value="0" />
</bean>
<bean id="txProxyTemplate" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
<bean id="userService" parent="txProxyTemplate">
<property name="target">
<bean class="tld.mydomain.business.UserServiceImpl"/>
</property>
<property name="proxyInterfaces" value="tld.mydomain.business.UserService"/>
</bean>
<context:component-scan base-package="tld.mydomain"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor" />
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="" p:suffix=".jsp"/>
<bean name="jsonView" class="org.springframework.web.servlet.view.json.JsonView">
<property name="encoding" value="ISO-8859-1"/>
<property name="contentType" value="application/json"/>
</bean>
最後我控制器代碼:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.JsonView;
import tld.mydomain.sample.business.UserService;
@Controller
@RequestMapping("/exp/*")
public class ExperimentsController {
@Autowired
private UserService userService;
@Autowired
private JsonView jsonView;
@RequestMapping(value="/long", method = RequestMethod.GET)
public ModelAndView lang() {
ModelAndView mav = new ModelAndView(jsonView);
userService.longQuery("UserA");
userService.longQuery("UserB");
return mav;
}
}
更新:這裏是UserServiceImpl
public class UserServiceImpl extends AbstractCRUDServiceImpl<User, String> {
@SuppressWarnings("unchecked")
@Override
public List<User> longQuery(String username) {
String like = "0" + username + "-%";
return DAO.getSession().createCriteria(User.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).addOrder(Order.asc("name"))
.createCriteria("interests").add(Restrictions.like("userPrefixedId", like))
.createCriteria("community").add(Restrictions.like("userPrefixedAuthorId", like))
.createCriteria("member").add(Restrictions.like("userPrefixedGroupId", like))
.add(Restrictions.isNotEmpty("skills"))
.list();
}
}
(該查詢是故意慢,這樣我可以很容易地重現誤差具有同時運行多個請求,看到許多同步查詢是如何在數據庫中)
運行你還需要我AbstractCRUDServiceImpl和GenericCRUDDAO還有:
public abstract class AbstractCRUDServiceImpl<Entity extends PublishableEntity, PkID extends Serializable> implements CRUDService<Entity, PkID> {
protected GenericCRUDDAO<Entity, PkID> DAO = new GenericCRUDDAO<Entity, PkID>(dataType());
@Override
public void create(Entity entity) {
DAO.create(entity);
}
@Override
public void delete(Entity entity) {
DAO.create(entity);
}
@Override
public Entity read(PkID entityPk) {
return DAO.read(entityPk);
}
@Override
public void update(Entity entity) {
DAO.update(entity);
}
private Class<PkID> pkType = null;
@SuppressWarnings("unchecked")
public Class<PkID> pkType() {
if(pkType != null)
return pkType;
// Backup solution in case datatype hasn't been set
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
pkType = (Class<PkID>) paramType.getActualTypeArguments()[1];
} else if (type instanceof Class) {
pkType = (Class<PkID>) type;
}
return pkType;
}
private Class<Entity> dataType = null;
@SuppressWarnings("unchecked")
private Class<Entity> dataType() {
if(dataType != null)
return dataType;
// Backup solution in case datatype hasn't been set
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
dataType = (Class<Entity>) paramType.getActualTypeArguments()[0];
} else if (type instanceof Class) {
dataType = (Class<Entity>) type;
}
return dataType;
}
}
在GenericCRUDDAO,PublishableEntity是我所有的實體從哪裏下來。它有幾個簡單方便的方法,如檢查,如果該實體是有效的,在一個toString使用或類似的,我希望
public class GenericCRUDDAO<EntityType extends PublishableEntity, PkID extends Serializable> implements CRUDDAO<EntityType, PkID> {
public GenericCRUDDAO() {}
public GenericCRUDDAO(Class<EntityType> datatype) {
this.setDataType(datatype);
}
private static SessionFactory sessionFactory = null;
public void setSessionFactory(SessionFactory sf) {
System.err.println("Setting SessionFactory for class " + this.getClass().getName());
sessionFactory = sf;
}
private Session session = null;
public Session getSession() {
if(session != null) {
if(session.isOpen())
return session;
}
if(sessionFactory == null)
Util.logError("sessionFactory is null");
session = ((SessionFactory) sessionFactory).getCurrentSession();
return session;
}
public void create(EntityType entity)
{
getSession().save(entity);
}
@SuppressWarnings("unchecked")
public EntityType read(PkID id)
{
return (EntityType) getSession().get(dataType(), id);
}
public void update(EntityType entity)
{
getSession().update(entity);
}
public void delete(EntityType entity) {
getSession().delete(entity);
}
public void delete(PkID id)
{
EntityType entity = read(id);
getSession().delete(entity);
}
private Class<EntityType> dataType = null;
@SuppressWarnings("unchecked")
private Class<EntityType> dataType() {
if(dataType != null)
return dataType;
// Backup solution in case datatype hasn't been set
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
dataType = (Class<EntityType>) paramType.getActualTypeArguments()[0];
} else if (type instanceof Class) {
dataType = (Class<EntityType>) type;
}
return dataType;
}
public void setDataType(Class<EntityType> datatype) {
this.dataType = datatype;
}
}
的配置和代碼使其在什麼部位應該公佈VS保持自身顯而易見的是,爲什麼我似乎一次只能做一個查詢,而沒有將它們放在一個腳上。
乾杯
聶
沒問題,我已經更新了帖子,包括UserSerivceImpl,並AbstractCRUDSerivceImpl它延伸並GenericCRUDDAO它使用。非常感謝你回到我這麼快 – niklassaers 2009-10-27 09:47:22
呵呵,似乎我們發現了同樣的錯誤。非常感謝您的幫助。我不太確定發生了什麼事,但是當我點擊接受時,答案中的「投票」就下降到零,而當我再次按下「上」時,它會顯示「投票太舊,無法更改,除非編輯了此答案」 。任何機會,你會做一個小小的編輯,所以我可以再次投票呢? ;-) – niklassaers 2009-10-27 16:50:50
不用擔心,一個小編輯done..ta – 2009-10-27 18:06:26