2009-10-27 5 views
1

저는 Hibernate를 사용하여 쿼리를 수행하는 Spring 3.0.0.RC1에서 매우 간단한 REST 컨트롤러 메소드를 만들었습니다. 쿼리를 완료하는 데 약 10 초가 소요됩니다. 나는 내 컨트롤러에 두 가지 요청을 할 수 있도록 이걸 의도로 만들었습니다.두 개의 스레드에서 같은 서비스와 DAO 사용하기

그런 다음 두 요청을 실행하고 MySQL (내 DB 백엔드)에서 "전체 프로세스 목록 표시"라는 쿼리를 실행합니다. 그리고 놀랍게도 한 가지 요청 만 있습니다. 하나의 요청은 성공할 것이다. 하나의 요청은 "org.hibernate.SessionException : Session is closed!"예외로 실패 할 것이다. 두 개 이상의 요청을하면 하나만 성공하고 다른 요청은 같은 방식으로 실패합니다. 그리고 여러 번 있어야하지만 항상 한 번에 하나의 쿼리 만있을 것입니다.

어떻게 될 수 있습니까? 어떤 제안? UserServiceImpl

여기에 있습니다 :

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

UPDATE

는 여기에 내가 컨트롤러에 사용하는 구성은, 당신이 내 구성에 대해 조금 얘기하려면

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가 필요합니다 얼마나보고에 대한 오류 : 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; 
    } 
} 

는 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; 
    } 
} 

내가 구성 및 코드를 만들 희망 할 때 어떤 부분이 그 자체로 유지 대를 게시 할 경우에는 확인과 같은 몇 가지 간단한 편의성 방법이있다 왜 내가 한 번에 하나의 쿼리 만 수행 할 수있는 것처럼 보이는 것인가?

건배

답변

1

은 나에게 매우 표준 보인다.

  • JsonView는 스레드 안전 - 나는 그것이 UserService의 구현은 또한 서비스 싱글의 스타일로

일반적으로 스레드 안전됩니다

  • 생각하는 : 물론

    이 있다고 가정 당신이 회원의 상태를 유지하는 것과 같은 일을하지 않는 한 그들은 있습니다.

    내가 볼 수있는 것에서 GenericCRUDDAO 나는 회원 session에 세심한주의를 기울일 것입니다. GenericCRUDDAO가 싱글 톤 (하나의 도메인 객체 당 하나의 모양)이라면 거기에서 약간의 문제가 발생할 것입니다.이 SessionFactory에이 스레드 로컬 세션을 사용하고있는 가정, 스레드 안전해야

    public Session getSession() { 
        return ((SessionFactory) sessionFactory).getCurrentSession(); 
    } 
    

    :

    getSession()의 구현은 실제로 단락 될 수 있습니다. 서비스가 싱글이기 때문에

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

    :

    내 업데이 트를 작성 한 후
  • +0

    아니 문제는, 내가 그것을 사용하는 UserSerivceImpl 및 AbstractCRUDSerivceImpl가 연장 및 GenericCRUDDAO을 포함하도록 게시물을 업데이트했습니다. 너무 빨리 내게로 돌아와 주셔서 고마워요. – niklassaers

    +0

    Hehe, 우리는 같은 오류를 발견 한 것 같습니다. 당신의 도움을 주셔서 대단히 감사합니다. 나는 어떤 일이 일어 났는지 잘 모르겠다. 그러나 나는 당신의 대답에 대해 "투표"를 받아 들였을 때 "0"으로 내려 갔고, 다시 "위로"를 누르면 "이 답변을 편집하지 않으면" . 당신이 작은 편집을 할 수있는 기회라면 다시 투표 할 수 있습니까? ;-) – niklassaers

    +0

    걱정하지 마세요. 하나의 작은 편집을 완료했습니다. –

    1

    그것은 나를 공격 할 때까지, 내가보고 계속 무엇을 또 다시 반복해서 같은 코드를 찾고 있었어요 AbstractCRUDSerivceImpl로부터 상속 받았다. 그 뉴스는 DAO 인 "private session session"이 실제로 정적 인스턴스가된다. 그리고 "if (session.isOpen()) 세션 반환;" 경쟁 조건이됩니다. 지금 기능을 축소했습니다 :

    public Session getSession() { 
        return ((SessionFactory) sessionFactory).getCurrentSession(); 
    } 
    

    이것은 내 문제를 해결하는 것 같습니다. 이 문제는 해결책으로 보이나요? 아니면 여전히 눈에 띄는 다른 문제가 있습니까?

    건배

    +0

    답변을 볼 수 없었습니다 ... 이미 내 정보를 업데이트했습니다. –

    관련 문제