1

I'm using Spring and Hibernate with annotation-driven transactions. When running my application I've received an exception "createCriteria is not valid without active transaction". According to this Spring/Hibernate Exception: createCriteria is not valid without active transaction the solution is to remove the line <property name="current_session_context_class">thread</property> from sessionFactory configuration.

However, I also need to do some transactional work in my @PostConstruct method (initialization from DB). @PostConstruct methods can't be transactional, so I open a manual transaction - but this does not work when I remove the the above line (getting an exception org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here). According to several sources the solution to this is to add <property name="current_session_context_class">thread</property> to configuration...

This is my code (I'm aware it's not too nice and clean - I've been fiddling with it to understand what the problems were):

`@Transactional public class TaskControllerImpl implements 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;
    }
}

`

Configuration:

`

<!-- 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/> 

`

So I'm in a bit of a catch-22 here on how to make both init() and run() work. Any ideas?

Community
  • 1
  • 1
Alex
  • 1,041
  • 3
  • 14
  • 32
  • You should use a TransactionTemplate See http://stackoverflow.com/questions/17346679/transactional-on-postconstruct-method – pards Feb 14 '14 at 18:23

1 Answers1

1

You just need to extract your transactional method from the postconstruct, and then call it. For example:

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

@Transactional
public void init(){
    ...
}
aweigold
  • 6,532
  • 2
  • 32
  • 46
  • Thanks but it doesn't work (according to Spring documentation @Transactional is not invoked on internal method calls - only on calls outside of the bean). I'll probably use something like this though - define a `boolean initialized` and call init(without @postconstruct) from run() on the first invocation. – Alex Jan 26 '12 at 15:32
  • 1
    Maybe you're using mode=aspectj or proxy_target_class=true on the ? I think it works in this case, I wouldn't want to change just for this though – Alex Jan 26 '12 at 15:36
  • ah... you are correct, I am using proxy_target_class. You could do the init() in the PostConstruct of another bean, like a Configuration class. – aweigold Jan 26 '12 at 15:38