2012-01-26 147 views
1

我使用Spring和Hibernate註釋驅動事務。 運行我的應用程序時,我收到一個異常「createCriteria無效,沒有活動事務」。根據此Spring/Hibernate Exception: createCriteria is not valid without active transaction解決方案是從sessionFactory配置中刪除行<property name="current_session_context_class">thread</property>春季休眠交易 - 捕捉22

但是,我還需要在我的@PostConstruct方法(從數據庫初始化)中執行一些事務性工作。 @PostConstruct方法不能是事務性的,所以我打開一個手動事務 - 但是當我刪除上面的行時(得到一個異常org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here),這不起作用。根據幾個來源,解決方案是將<property name="current_session_context_class">thread</property>添加到配置中...

這是我的代碼(我知道它不是太好,很乾淨 - 我一直在弄清楚它是如何理解問題的):

`@Transactional 公共類TaskControllerImpl實現TaskController {

@Autowired 
TaskDAO taskDAO; 

@Autowired 
MethodDAO methodDAO; 

@Autowired 
SessionFactory sessionFactory; 

ExecutorService threadPool = Executors.newCachedThreadPool(); 


/** 
* Map of method name to TaskExecutor for all defined methods 
*/ 
private Map<String, TaskExecutor> executorsByMethod; 

final Logger logger = LoggerFactory.getLogger(TaskControllerImpl.class); 

/** 
* Initializes the mapping of method name to TaskExecutor 
*/ 
@PostConstruct 
public void init() { 
    // @Transactional has no effect in @Postconstruct methods so must do this manually 
    Transaction t = sessionFactory.getCurrentSession().beginTransaction(); 
    executorsByMethod = new HashMap<String, TaskExecutor>(); 
    List<Method> methods = methodDAO.findAll(); 
    for (Method method : methods) {   
     if (method.getExecutorClassName() != null) { 
      try { 
       TaskExecutor executor = createTaskExecutor(method); 
       executorsByMethod.put(method.getName(), executor); 
      } catch (Throwable e) { 
       logger.error("Coud not create/instantiate executor " + method.getExecutorClassName()); 
       e.printStackTrace(); 
      } 
     }  
    } 

    t.commit(); 
} 

@Override 
public void run() { 
    Collection<Task> tasksToExecute = fetchTasksToExecute(); 
    for (Task task : tasksToExecute) { 
     String method = task.getMethod().getName(); 
     executorsByMethod.get(method).addTask(task); 
    } 
} 

/** 
* Fetches all tasks which need to be executed at the current time 
* 
* @return 
*/ 
private Collection<Task> fetchTasksToExecute() { 
    try { 
     Search search = new Search(); 
     search.addFilterLessThan("actionDate", DateUtil.now()); 
     search.addFilterEqual("status", TaskStatus.PENDING.getCode()); 
     search.addSort("actionDate", false); 
     return taskDAO.search(search); 
    } catch (Throwable e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
     return null; 
    } 
} 

`

配置:

`

<!-- Configure a JDBC datasource for Hibernate to connect with --> 
<bean id="dataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 
    <property name="url" value="${connection.url}" /> 
    <property name="username" value="${connection.username}" /> 
    <property name="password" value="${connection.password}" /> 
</bean> 

<!-- Configure a Hibernate SessionFactory --> 
<bean id="sessionFactory" 
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="packagesToScan" value="com.grroo.model" /> 
    <property name="hibernateProperties"> 
     <props> 
      <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> 
      <prop key="hibernate.show_sql">false</prop> 
      <!-- prop key="hibernate.current_session_context_class">thread</prop--> 
      <prop key="hibernate.connection.zeroDateTimeBehavior">convertToNull</prop> 
     </props> 
    </property> 
</bean> 

<bean id="transactionManager" 
    class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="sessionFactory" /> 
</bean> 

<tx:annotation-driven/> 

`

所以我在有點左右爲難這裏就如何使兩者的init()和run()的工作。有任何想法嗎?

+0

您應該使用TransactionTemplate的 見http://stackoverflow.com/questions/17346679/transactional-on-postconstruct-method – pards

回答

1

你只需要從postconstruct中提取你的交易方法,然後調用它。例如:

@PostConstruct 
public void postConstruct(){ 
    init(); 
} 

@Transactional 
public void init(){ 
    ... 
} 
+0

謝謝,但它不工作(根據春文檔@Transactional不在內部方法調用上調用 - 僅在bean外調用時)。我可能會使用類似這樣的東西 - 定義一個'boolean initialized'並在第一次調用時從run()調用init(沒有@postconstruct)。 – Alex

+0

我將編輯...也許我不清楚。 – aweigold

+1

也許你在上使用mode = aspectj或proxy_target_class = true?我認爲它在這種情況下起作用,我不想僅僅爲了這個而改變 – Alex