When using #Transactionnal with Spring
How does it select the datasource on which it should open the transaction?
Is there some magic trick like proxies, threadlocals or anything?
If so, do these tricks work with any JDBC library (for Hibernate it works but what about MyBatis?)
What if there are 2 datasources?
And what if i'm calling, in a #Transactionnal service, DAO's with 2 underlying different datasources? Will it be transactionnal for both datasources or just for one of them or will it fail?
Thanks
To use multiple transaction handlers just specifiy a qualifier, and reference it. For two different DAOs with two different datasources, you'll need two different transaction managers. And of course the transactions should be going on your service classes, not DAO's directly. Its the same for any type of transactionmanager be it hibernate or plain old jdbc.
<bean id="transactionManagerOne"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactoryOne">
<qualifier value="One" />
</bean>
and session factory
<bean id="sessionFactoryOne"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSourceOne"
and just set up a datasource with the ID dataSourceOne, then you cran reference the transactionmanager in code with the qualifier name :
#Transactional(value = "One")
I can provide a partial answer which is that usually when you use a PlatformTransactionManager from Spring it was associated with a single datasource when it was created. Something like this:
#Bean public PlatformTransactionManager txManager() {
return new HibernateTransactionManager(sessionFactory());
}
The SessionFactory is configured with a datasource. I suspect if you want to have multiple PlatformTransactionManagers then you can't just rely on autowiring them as beans like I did above. You might have to use the TransactionTemplate class instead and code on a slightly lower level.
Related
#EnableTransactionManagement will scann all Spring Beans for #Transactional in order to provide transaction management.
Is it possible to provide list of the packages, that will be scanned?
For example: I would like to have support for #Transactional in package a.b.c but I would like to ignore classes marked with #Transactional in package x.y.z.
The reason for such wird requirement is 3-party lib. It has #Transactional(readonly=false) on almost every class, but it does only in-memory operations. Every time when I call this API spring obtains DB connection, and I really need to avoid that. I would like to exclude this whole lib from transaction management. I also cannot create separate spring context for this lib.
As far as I know you can define your custom transaction manager in the configuration file:
Example:
<tx:annotation-driven transaction-manager="txManagerRest" proxy-target-class="true"/>
<bean id="txManagerRest" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceRest"/>
</bean>
Then you can set in the classes you want this custom transaction manager like:
#Transactional("txName")
or
#Transactional(value = "txName")
In this way you would be using transactional where you want.
Moreover, you can now have as many transaction managers as you want and use it accordingly wherever you need.
I did some research on how to test my DAO layer using HyperSQL and I found this question: https://softwareengineering.stackexchange.com/questions/219362/how-to-test-the-data-access-layer
How do I import DBConnection because I tried using hypersql jar and it did not work. In the question from the link I saw this db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true; but I do not know how to use it.
Using JDBC and Spring, in your application you'd be creating a DataSource object, and using that object to create a JdbcTemplate object, which is what you're using to run your JDBC queries. Similar to this example. So in order to use the HSql database, you'd need to change the values used to setup the DataSource bean but only for your unit tests.
Depending on how you're using Spring (annotations, xml config), there's a few ways of doing it. You'll need to create a new configuration bean, or configuration xml file, and then reference it in your unit tests.
For XML, copy over your spring context xml file, and change the DataSource values to something like:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;"/>
<property name="username" value="SA"/>
<property name="password" value=""/>
You'll then need to reference this new context config in your unit test. Something like:
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/test-config.xml"
#ContextConfiguration("/test-config.xml")
public class DAOTests {
#Autowired
private DatabaseDAO dao;
}
With annotations, you'll need to make a new configuration class (you should have one already, a class annotated with #Configuration) and return the appropriate DataSource. Something like this:
#Bean
public DataSource hsqlDataSource(){
return DataSourceBuilder
.create()
.driverClassName("org.hsql.jdbcDriver")
.url("jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;")
.username("SA")
.password("");
.build();
}
You can setup a profile for your configuration (using #Profile("test") annotation), and then specify which profile you want to use in your unit test (#ActiveProfiles("test")). More information here.
Alternatively, you could use the Spring EmbeddedDatabaseBuilder if you're just trying to do a simple DAO Test. There's a tutorial here. It sets you up with a HSql in memory database (as opposed to a file based one), which is very convenient for setting up / tearing down. Just add the bean to your configuration class, and your DAO should pick it up if Spring is injecting the DataSource for you. Example below taken from here
#Bean
public DataSource dataSource() {
// no need shutdown, EmbeddedDatabaseFactoryBean will take care of this
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("db/setup_script.sql")
.build();
}
There's a bit to Spring configuration management. I haven't covered it in much detail in this answer, but look up a few examples and it will start to make sense.
I want to inject a map containing all the properties that spring knows of (which are inserted by a library) to a config class that I have through the spring xml. Is that possible?
<bean class="Config">
<constructor-arg name="env">
<map>
//inject all properties?
</map>
</constructor-arg>
</bean>
Why don't you just inject the Spring Context? Through the Context, you can look up any bean via its name.
Edit:
From this answer, you could also use the following:
<bean class="Config">
<constructor-arg name="env">
<util:properties location="${path.to.properties.file}"/>
</constructor-arg>
</bean>
Where your "env" constructor argument is a java.util.Properties object.
For later versions of Spring (including spring-boot) that support the injection of an Environment you can use this to access all properties loaded.
To answer this question inject a AbstractEnvironment so that you are able to call the getPropertySources() method that will allow you to see where the properties have been loaded from (e.g. a file, OS variables, etc)
#Autowired
public Config(AbstractEnvironment environment)
{
MutablePropertySources propertySources = environment.getPropertySources();
// inspect propertySources to see all properties loaded by Spring
}
Can you not extend the library class that you use and instantiate your bean instead of the default library one? Then you would be able to inspect all the values.
Otherwise, if you know the signature of the library, you can always use AOP to weave some code around the library and get access to the properties there. A bit more complicated, but still gets you where you need to go. You can definitely use AspectJ (which requires a little more config) or even Spring AOP, depending how things are being accessed.
If you want/need more insight on this, let me know.
I have a properties set like so :
<context:property-placeholder
location="file:${catalina.home}/conf/my.properties"
ignore-unresolvable="true" />
they are then referenced in app context (specifically app.email) like so :
<bean id="alertMailMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="to">
<value>${app.email}</value>
</property>
</bean>
However when I try to access that property within an actual pojo, not a spring bean - actually a pojo annotated as a hibernate entity (not the alertMailMessage bean) it is coming back as null ?
#Value("${app.email}")
private String defaultEmailAddress;
I want to use the value of property setting "app.email" elsewhere, other than alertMailMessage, whats the best way ? (alertMailMessage is working fine btw)
You can't set it in a hibernate entity, because hibernate entities are not managed by spring.
Use the #Value annotation in your spring service which creates the hibernate entity, and set it manually if needed. But it looks odd to store a default value in the database, so reconsider that.
As a sidenote: you can have hibernate entities managed by spring if using aspectJ and #Configurable, but that may complicate things unnecessarily.
I have an application that I am currently writing that will use Spring and Hibernate. In my services layer I have injected a DAO that will do some very basic CRUD-ing actions. For grins, I have created a method annotated as follows:
#Transactional(readOnly = false, propogation=Propogation.REQUIRES_NEW)
public void doSomeWork(Dao dao, Entity e){
//do some searching
dao.persist(e);
dao.findAll(Entity.clz);
}
The dao persist method looks like this:
public void persist(Entity e){
session.saveOrUpdate(e); //This has already been built using a SessionFactory
}
The dao findAll method looks like this
public void findAll(Class clz) {
session.createCriteria(clz).list();
}
Now, everything seems to run, OK. After I insert (persist) my object, I can see it using the findAll method (along with the new Primary Key ID that it was assigned by the Data Store), however, when the "doSomeWork" method completes, my data does not actually get persisted to the underlying datastore (Oracle 10g).
If, however, I remove the #Transactional annotations and use Hibernate's session.getTransaction().begin() and session.getTransaction().commit() (or rollback), the code works as I would anticipate.
So, my underlying question would then be: Does Hibernate not actually use Spring's transaction management for actual transaction management?
In my bean-config file I am declaring a TransactionManager bean, a SessionFactory bean, and I am also including in the config file.
What could I possibly be missing, aside for a better working-knowledge of Spring and Hibernate?
Don't forget to add <tx:annotation-driven> for #Transactional support
sounds like spring doesnt actually inject the transaction handling code.
do you have something like this in your config file, to tell spring where to look for annotated classes?
<beans xmlns:context="http://www.springframework.org/schema/context" ...
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd ..." >
...
<context:annotation-config/>
<context:component-scan base-package="mypackage.dao.impl"/>
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
what method do you use to obtain the session object from the session factory? Are you using openSession(), or getCurrentSession(), or perhaps something else? This matters because you need to session to be bound to the spring transaction (I doubt if openSession is good for your scenario)
I suggest that you use Spring's hibernateTemplate to invoke saveOrUpdate and persist, instead of using the session object. This way you are guaranteed that it will be bound to the transaction, and, as spring promises, you won't need to change the code if you ever change the transaction management strategy.
Ok, well, thanks to everyone who responded... it helped me to figure out what I am doing wrong...
In my overzealous "proof-of-concepting" it never really dawned on me what was going on until I realized that my "simple java class with a main method that will be doing all my work" isn't managed by spring, therefore no real transaction management. This will in no way behave as a product application would, being managed by an app-server with controller beans, services, etc.
Then it dawned on me... "services"... I'm going to have a services layer, that's where the transaction support will live. So, right as rain, I created a simple service bean (marked with #Transactional) and it works just as I would hope it would. So, I call methods on my service, which calls methods on my Dao and bam!!! it works.
Thanks again to everyone who helped me to come to this conclusion.