Activate #Transactional only for predefined packages using Java Config - java

#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.

Related

Spring unable to wiring bean for "Generic Dao without special implementation class" case

I try to implement solution for Generic DAO like this https://stackoverflow.com/a/511417.
However for “Using this genericDAO without special implementation Class” case I get the NoSuchBeanDefinitionException.
The full stacktrace http://pastebin.com/HwrjEZiX
As I see Spring can't wiring bean for Generic Dao without special implementation class
https://dl.dropboxusercontent.com/u/8384811/Misc/2013-05-14_224944.jpg
Spring uses the JdkDynamicAopProxy for wiring BranchHibernateDao class, “Using this genericDAO with special implementation Class” case.
According JavaDoc it Creates a dynamic proxy, implementing the interfaces exposed by
* the AopProxy. Dynamic proxies cannot be used to proxy methods
defined in classes, rather than interfaces.
So it sees the methods from BrunchDao and Crud interfaces for branchDao bean definition.
However it can't wire the branchGenericDao (“Using this genericDAO without special implementation Class” case) and don't see the Crud interface methods.
I'll appreciate for any help!
Bean's wiring
<bean id="branchDao" class="org.jtalks.poulpe.model.dao.hibernate.BranchHibernateDao" parent="genericDao"/>
<bean id="branchGenericDao" class="org.jtalks.common.model.dao.hibernate.GenericDao">
<qualifier value="branchGenericDao"/>
<constructor-arg name="sessionFactory" ref="sessionFactory"/>
<constructor-arg name="type" value="org.jtalks.poulpe.model.entity.PoulpeBranch"/>
</bean>
<bean id="genericDao" abstract="true" class="org.jtalks.common.model.dao.hibernate.GenericDao">
<constructor-arg name="sessionFactory" ref="sessionFactory"/>
</bean>
Test source is here https://github.com/jtalks-org/poulpe/blob/master.senleft/poulpe-model/src/test/java/org/jtalks/poulpe/model/dao/hibernate/BranchHibernateDaoTest.java
Crud source is here https://github.com/jtalks-org/jtalks-common/blob/master.senleft/jtalks-common-model/src/main/java/org/jtalks/common/model/dao/Crud.java
GenericDao source is here https://github.com/jtalks-org/jtalks-common/blob/master.senleft/jtalks-common-model/src/main/java/org/jtalks/common/model/dao/hibernate/GenericDao.java
BranchHibernateDao source is here https://github.com/jtalks-org/poulpe/blob/master.senleft/poulpe-model/src/main/java/org/jtalks/poulpe/model/dao/hibernate/BranchHibernateDao.java
The problem is probably with your test. When you write tests with Spring, you have to use the proper Spring test runner. Try modifying your test to work more like this, or try actually implementing it and not doing it with a testing framework to see if it works.
Also, if you want to create generic daos and you're using Hibernate, you should REALLY look into Spring Data JPA. Using it will make your life 10x easier than doing what you're trying to do, since it does what you're trying to do, but with less work and better options than you'll be able to implement by yourself.
(The answer you're following pre-dates Spring Data. I'm sure they would've used Spring Data if it existed.)

With Spring #Transactionnal, how do Spring know which datasource to use?

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.

How to inject all properties from Spring into a bean?

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.

Why I have to declare each and every class in my hibernate.cfg.xml when using annotations?

Why it isn't enough to set the #Entity annotation?
Am I missing the point here e.g. performance?
The annotation is not enough because hibernate does not know where your annotated classes live without some sort of explicit declaration. It could, in theory, scan every single class in the classpath and look for the annotation but this would be very very expensive for larger projects.
You can use spring which has a helper that can allow you to specify the package(s) that your hibernate objects are in and it will just scan these packages for #Entity. If you have all your objects in a small number of fixed packages this works well.
E.g.
<bean id="referenceSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<array>
<value>com.xxx.hibernate.objects</value>
</array>
</property>
</bean>
The above is the Spring declaration. If you aren't familiar with the above syntax you can just construct it programmatically.
AnnotationSessionFactoryBean sfb = new AnnotationSessionFactoryBean();
sfb.setDataSource( ds );
sfb.setHibernateProperties( hibProps);
sfb.setPackagesToScan( ... );
sfb.initialise();
SessionFactory sf = sfb.getObject();
It supports a bunch of config options so you can use raw properties or pass in a pre-config'd datasource.
You don't if you set hibernate.archive.autodetection property to true. There is a performance issue here as Hibernate will search the jar files for the JPA annotation. Note that, it will also initializes those classes.
Yes :)
The hibernate.cfg.xml file is not used to specify your entities, it's used to configure things like hibernate's connection parameters and global settings. The hibernate.cfg.xml file also contains instructions on how to locate the entities. You can list the XML mapping files using <mapping resource=XYZ>, but if you're using JPA annotations like #Entity, then this is unnecessary, Hibernate will auto-detect them.
You can mix annotations and mapping XML if you choose, but that's by no means necessary in most situations.
By default all properly annotated
classes and all hbm.xml files found
inside the archive are added to the
persistence unit configuration. You
can add some external entity through
the class element though.
Hibernate EntityManager Reference Docs

Data does not persist using HIbernate with Spring's #Transactional Annotation

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.

Categories

Resources