I'm writing a simple bean that I want to configure with a table name, an XML file with some data, so that if at application startup the table is empty it get initialized with that data. I decided to use simple SQL queries, but I can't get a session from the sessionfactory, because it says:
Error creating bean with name 'vecchiOrdiniFiller' defined in ServletContext resource [/WEB-INF/spring/servlet-context.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
But this is the configuration (very similar to a service):
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<tx:annotation-driven />
<bean id="ordiniVecchioSistemaLoader" class="it.jsoftware.jacciseweb.assistenza.common.ExcelXmlDataLoader">
<property name="xmlFileName" value="WEB-INF/data/daticlientijaccisemarco.xml"></property>
</bean>
<bean id="vecchiOrdiniFiller" class="it.jsoftware.jacciseweb.assistenza.common.BaseTableFiller" init-method="init">
<property name = "sessionFactory" ref = "mySessionFactory"></property>
<property name="loader" ref="ordiniVecchioSistemaLoader"></property>
<property name="tableCreationString" value="CREATE TABLE `vecchiordini` ( `ID` INT(11) NOT NULL AUTO_INCREMENT, `codicejazz` VARCHAR(255) DEFAULT NULL, `progressivolicenza` INT(11), `codicearticolo` VARCHAR(255) DEFAULT NULL, `rivenditore` VARCHAR(255) DEFAULT NULL, `cliente` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`ID`)) ENGINE=INNODB DEFAULT CHARSET=utf8"></property>
<property name="table" value="vecchiordini"></property>
<property name="tableColumns">
<list>
<value>codicejazz</value>
<value>progressivolicenza</value>
<value>codicearticolo</value>
<value>rivenditore</value>
<value>nomecliente</value>
</list>
</property>
<property name="loaderColumns">
<list>
<value>clicod</value>
<value>licsmatricola</value>
<value>artcod</value>
<value>rivenditore</value>
<value>cliente</value>
</list>
</property>
</bean>
and I annotated the init() method with @Transactional. But it doesn't start a transaction and I get that error:
@Transactional
public void init() throws Exception {
logger.info("BaseTableFilter per tabella: " + table + ", usando: "
+ loader.getSourceName());
Session session = dao.getSession();
Transaction tx = session.beginTransaction();
...
why doesn't that work?
At a high level, Spring creates proxies for all the classes annotated with @Transactional, either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method, mainly for starting and committing the transaction.
You certainly can place the @Transactional annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies.
If you're using Spring without Spring Boot, you need to activate the transaction management by annotating your application class with @EnableTransactionManagement. Here you can see a simple example of a service with a transactional method.
Annotation Type Transactional. Describes a transaction attribute on an individual method or on a class. When this annotation is declared at the class level, it applies as a default to all methods of the declaring class and its subclasses.
The init-method
or @PostConstruct
annotated methods are not proxied, so you will not be able to use @Transactional on that. You should use @Transactional on your service, why is that not suitable for you?
Quoted from the SpringSource Jira:
This is as defined, actually: init methods (such as @PostConstruct methods) are always called on the target instance itself. The proxy will only be generated once the target instance has been fully initialized... In other words, the @Transactional proxy isn't even created at the point of the @PostConstruct call yet.
Switching to mode="aspectj" would help since it weaves the target class directly, in which case the init method will have been modified for transactional awareness at the time of the container init call already.
I guess at the very minimum, we should document the limitations of @Transactional on proxies more clearly - and point out where mode="aspectj" might be a solution.
As stated, you can try the mode="aspectj"
but rather you should review your design in my opinion.
I don't think you can make the init method transactional. But you can invoke another transactional method of another service from it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With