My application data access layer is built using Spring and EclipseLink and I am currently trying to implement the following feature - Ability to switch the current/active persistence unit dynamically for a user. I tried various options and finally ended up doing the following.
In the persistence.xml, declare multiple PUs. Create a class with as many EntityManagerFactory attributes as there are PUs defined. This will act as a factory and return the appropriate EntityManager based on my logic
public class MyEntityManagerFactory {
#PersistenceUnit(unitName="PU_1")
private EntityManagerFactory emf1;
#PersistenceUnit(unitName="PU_2")
private EntityManagerFactory emf2;
public EntityManager getEntityManager(int releaseId) {
// Logic goes here to return the appropriate entityManeger
}
}
My spring-beans xml looks like this..
<!-- First persistence unit -->
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="emFactory1">
<property name="persistenceUnitName" value="PU_1" />
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager1">
<property name="entityManagerFactory" ref="emFactory1"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager1"/>
The above section is repeated for the second PU (with names like emFactory2, transactionManager2 etc).
I am a JPA newbie and I know that this is not the best solution. I appreciate any assistance in implementing this requirement in a better/elegant way!
Thanks!
First of all thanks to user332768 and bert. I tried using AbstractRoutingDataSource as mentioned in the link provided by bert, but got lost trying to hook up my jpa layer (eclipselink). I reverted to my older approach with some modifications. The solution looks cleaner (IMHO) and is working fine. (switching database at runtime and also writing to multiple databases in the same transaction)
public class MyEntityManagerFactoryImpl implements MyEntityManagerFactory, ApplicationContextAware {
private HashMap<String, EntityManagerFactory> emFactoryMap;
public EntityManager getEntityManager(String releaseId) {
return SharedEntityManagerCreator.createSharedEntityManager(emFactoryMap.get(releaseName));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
Map<String, LocalContainerEntityManagerFactoryBean> emMap = applicationContext.getBeansOfType(LocalContainerEntityManagerFactoryBean.class);
Set<String> keys = emMap.keySet();
EntityManagerFactory entityManagerFactory = null;
String releaseId = null;
emFactoryMap = new HashMap<String, EntityManagerFactory>();
for (String key:keys) {
releaseId = key.split("_")[1];
entityManagerFactory = emMap.get(key).getObject();
emFactoryMap.put(releaseId, entityManagerFactory);
}
}
}
I now inject my DAO's with an instance (singleton) of MyEntityManagerFactoryImpl. The dao will then simply call createSharedEntityManager with the required release and will get the correct EntityManager for that database. (Note that i am now using application managed EntityManager and hence i have to explicitly close them in my dao)
I also moved to jta transaction manager (to manage transaction across multiple databases)
This is how my spring xml looks like now.
...
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="em_Rel1">
<property name="persistenceUnitName" value="PU1" />
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="em_Rel2">
<property name="persistenceUnitName" value="PU2" />
</bean>
<bean class="org.springframework.transaction.jta.JtaTransactionManager" id="jtaTransactionManager">
</bean>
<tx:annotation-driven transaction-manager="jtaTransactionManager"/>
....
Cheers! (comments are welcome)
I am not sure if this is a clean method. Instead of declaring the enitiymanagerfactory multiple times, we can use the spring application context to get the entitymanagerfactory declared in the spring application.xml.
hm = applicationContext.getBeansOfType(org.springframework.orm.jpa.LocalEntityManagerFactoryBean.class);
EntityManagerFactory emf = ((org.springframework.orm.jpa.LocalEntityManagerFactoryBean) hm.get("&emf1")).getNativeEntityManagerFactory();
EntityManagerFactory emf2 = ((org.springframework.orm.jpa.LocalEntityManagerFactoryBean) hm.get("&emf2")).getNativeEntityManagerFactory();
This is something i need to do in the future too, for this i have bookmarked Spring DynamicDatasourceRouting
http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/
As far as i understand, this is using one PU, which gets assigned different DataSources. Perhaps it is helpful.
Related
I am working on a migration project which involves upgrading the platform to Spring 4 with MyBatis. In the legacy code, transactions are handled at a central locations wherein call to start/end transactions are spread across various classes like service class, helper class and DAO class.
I managed to convert all service classes to spring managed component and DAO classes to support MyBatis-spring API. Problem is my service class use several other classes to perform a function and those classes are all instantiated manually and used. Now if i start a transaction on service class methods and perform database transactions inside other helper or DAO classes which are not spring managed, my transaction handling doesn't work correctly. I have illustrated this problem in the below code. Could you tell what are the ways to acheive transaction handling without modifying the code?
Example :
package com.service;
#Service
class MyService {
#Transactional( propagation=Propagation.REQUIRED)
public void processRequest () {
HelperClass helper = new HelperClass();
helper.performOperation();
}
}
package com.helper;
// this class is not spring bean
class HelperClass {
// MyBatis mapper class
private EmployeeMapper mapper;
public HelperClass () {
mapper = // retrieve mapper class bean from spring context
}
public performOperation () {
// call to mapper class insert operation
// call to mapper class update operation
}
}
package com.dao;
#Component
interface EmployeeMapper {
// method definition to perform database operation
}
Spring configuration details:
<context:component-scan base-package="com" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
....
....
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" />
<mybatis:scan base-package="com.dao" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations"
value="classpath*:mybatis/*.xml" />
</bean>
In the above code HelperClass.performOperation() method is doing 2 database operations (insert,update). Say if insert succeeds and update fails, my database transaction doesn't get rollback. Since I already started the transaction at MyService.processRequest() should this not rollback the operations that are carried inside that method call? Correct me if my understanding is wrong.
I am working in a project using Spring, Spring Data JPA, Spring Security, Primefaces...
I was following this tutorial about dynamic datasource routing with spring.
In this tutorial, you can only achieve dynamic datasource switching between a pre-defined datasources.
Here is a snippet of my code :
springContext-jpa.xml
<bean id="dsCgWeb1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName.Cargest_web}"></property>
<property name="url" value="${jdbc.url.Cargest_web}"></property>
<property name="username" value="${jdbc.username.Cargest_web}"></property>
<property name="password" value="${jdbc.password.Cargest_web}"></property>
</bean>
<bean id="dsCgWeb2" class="org.apache.commons.dbcp.BasicDataSource">
// same properties, different values ..
</bean>
<!-- Generic Datasource [Default : dsCargestWeb1] -->
<bean id="dsCgWeb" class="com.cargest.custom.CargestRoutingDataSource">
<property name="targetDataSources">
<map>
<entry key="1" value-ref="dsCgWeb1" />
<entry key="2" value-ref="dsCgWeb2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dsCgWeb1" />
</bean>
What i want to do is to make the targetDataSources map dynamic same as its elements too.
In other words, i want to fetch a certain database table, use properties stored in that table to create my datasources then put them in a map like targetDataSources.
Is there a way to do this ?
Nothing in AbstractRoutingDataSource forces you to use a static map of DataSourceS. It is up to you to contruct a bean implementing Map<Object, Object>, where key is what you use to select the DataSource, and value is a DataSource or (by default) a String referencing a JNDI defined data source. You can even modify it dynamically since, as the map is stored in memory, AbstractRoutingDataSource does no caching.
I have no full example code. But here is what I can imagine. In a web application, you have one database per client, all with same structure - ok, it would be a strange design, say it is just for the example. At login time, the application creates the datasource for the client and stores it in a map indexed by sessionId - The map is a bean in root context named dataSources
#Autowired
#Qualifier("dataSources");
Map<String, DataSource> sources;
// I assume url, user and password have been found from connected user
// I use DriverManagerDataSource for the example because it is simple to setup
DataSource dataSource = new DriverManagerDataSource(url, user, password);
sources.put(request.getSession.getId(), dataSource);
You also need a session listener to cleanup dataSources in its destroy method
#Autowired
#Qualifier("dataSources");
Map<String, DataSource> sources;
public void sessionDestroyed(HttpSessionEvent se) {
// eventually cleanup the DataSource if appropriate (nothing to do for DriverManagerDataSource ...)
sources.remove(se.getSession.getId());
}
The routing datasource could be like :
public class SessionRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
return request.getSession().getId();
}
#Autowired
#Qualifier("dataSources")
public void setDataSources(Map<String, DataSource> dataSources) {
setTargetDataSources(dataSources);
}
I have not tested anything because it would be a lot of work to setting the different database, but I thing that it should be Ok. In real world there would not be a different data source per session but one per user with a count of session per user but as I said it is an over simplified example.
The datasource used by a thread might change from time to time.
Should pay attention to concurrency, applications might get concurrency issues in concurrent environment.
thread-bound AbstractRoutingDataSource sample
It can be achieved with AbstractRoutingDataSource and keeping the information in the thread-local Variable. Here is a beautiful working example you can refer to:
Multi-tenancy: Managing multiple datasources with Spring Data JPA
I am already using Hibernate 4 directly with a LocalSessionFactoryBean and a SessionFactory in my code.
I would now like to include Spring-Data-JPA in my code.
But Spring-Data needs an EntityManagerFactory to work, which can be configured through a LocalContainerEntityManagerFactoryBean. Can these Beans LocalSessionFactoryBean and LocalContainerEntityManagerFactoryBean coexist in one Spring project?
(Or can one be adapted by the other?)
What is the best practice?
Although they can coexists it will be problematic especially if you want to have them participate in the same transaction. However if you switch your logic around and configure a LocalContainerEntityManagerFactoryBean instead of a LocalSessionFactoryBean you can use the HibernateJpaSessionFactoryBean to get access to the underlying SessionFactory.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- Your properties here -->
</bean>
<bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Now you have both and can participate in the same transaction.
This solution is also documented in the Spring Data JPA reference guide in the FAQ section.
#Autowired
private EntityManager entitymanager;
public List<SpBooking> list() {
// #SuppressWarnings("unchecked")
System.out.println("******************************");
// #SuppressWarnings("unchecked")
List<SpBooking> listUser = (List<SpBooking>)((Session)entitymanager.getDelegate())
.createCriteria(SpBooking.class)
.list();
for (SpBooking i:listUser)
System.out.println("------------------"+i.getBookingId());
return listUser;
}
And as of JPA 2.1, EntityManagerFactory.unwrap(java.lang.Class) provides a nice approach, documented here: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/vendor/HibernateJpaSessionFactoryBean.html
#Bean
public SessionFactory sessionFactory(#Qualifier("entityManagerFactory") EntityManagerFactory emf) {
return emf.unwrap(SessionFactory.class);
}
I have the following test..
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/schedule-agents-config-context.xml"})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
#Transactional
public class H2TransactionNotWorkingTest extends SubmitAgentIntegratedTestBase {
private static final int FIVE_SUBMISSIONS = 5;
#Autowired
private ApplicationSubmissionInfoDao submissionDao;
private FakeApplicationSubmissionInfoRepository fakeRepo;
#Before
public void setUp() {
fakeRepo = fakeRepoThatNeverFails(submissionDao, null);
submitApplication(FIVE_SUBMISSIONS, fakeRepo);
}
#Test
#Rollback(true)
public void shouldSaveSubmissionInfoWhenFailureInDatabase() {
assertThat(fakeRepo.retrieveAll(), hasSize(FIVE_SUBMISSIONS));
}
#Test
#Rollback(true)
public void shouldSaveSubmissionInfoWhenFailureInXmlService() {
assertThat(fakeRepo.retrieveAll().size(), equalTo(FIVE_SUBMISSIONS));
}
}
...and the following config...
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:/db/h2-schema.sql" />
</jdbc:embedded-database>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionalSessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
</props>
</property>
<property name="namingStrategy">
<bean class="org.hibernate.cfg.ImprovedNamingStrategy"/>
</property>
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>
<property name="packagesToScan" value="au.com.mycomp.life.snapp"/>
</bean>
<bean id="regionDependentProperties" class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="region-dependent-service-test.properties"/>
</bean
>
I have also set auto commit to false in the sql script
SET AUTOCOMMIT FALSE;
There are not REQUIRES_NEW in the code.
Why is the rollback not working in the test?
Cheers
Prabin
I have faced the same problem but I have finally solved it albeit I don't use Hibernate (shouldn't really matter).
The key item making it work was to extend the proper Spring unit test class, i.e. AbstractTransactionalJUnit4SpringContextTests. Note the "Transactional" in the class name. Thus the skeleton of a working transactional unit test class looks like:
#ContextConfiguration(locations = {"classpath:/com/.../testContext.xml"})
public class Test extends AbstractTransactionalJUnit4SpringContextTests {
#Test
#Transactional
public void test() {
}
}
The associated XML context file has the following items contained:
<jdbc:embedded-database id="dataSource" type="H2" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
Using this setup the modifications by each test method is properly rolled back.
Regards, Ola
I'm experiencing similar problems, I'm also using TestNG + Spring test support and Hibernate. What happens is that Hibernate disables autocommit on the connection before the transaction begins and it remembers the original autocommit setting:
org.hibernate.engine.transaction.internal.jdbc#JdbcTransaction:
#Override
protected void doBegin() {
try {
if ( managedConnection != null ) {
throw new TransactionException( "Already have an associated managed connection" );
}
managedConnection = transactionCoordinator().getJdbcCoordinator().getLogicalConnection().getConnection();
wasInitiallyAutoCommit = managedConnection.getAutoCommit();
LOG.debugv( "initial autocommit status: {0}", wasInitiallyAutoCommit );
if ( wasInitiallyAutoCommit ) {
LOG.debug( "disabling autocommit" );
managedConnection.setAutoCommit( false );
}
}
catch( SQLException e ) {
throw new TransactionException( "JDBC begin transaction failed: ", e );
}
isDriver = transactionCoordinator().takeOwnership();
}
Later on, after rolling back the transaction, it will release the connection. Doing so hibernate will also restore the original autocommit setting on the connection (so that others who might be handed out the same connection start with the original setting). However, setting the autocommit during a transaction triggers an explicit commit, see JavaDoc
In the code below you can see this happening. The rollback is issued and finally the connection is released in releaseManagedConnection. Here the autocommit will be re-set which triggers a commit:
org.hibernate.engine.transaction.internal.jdbc#JdbcTransaction:
#Override
protected void doRollback() throws TransactionException {
try {
managedConnection.rollback();
LOG.debug( "rolled JDBC Connection" );
}
catch( SQLException e ) {
throw new TransactionException( "unable to rollback against JDBC connection", e );
}
finally {
releaseManagedConnection();
}
}
private void releaseManagedConnection() {
try {
if ( wasInitiallyAutoCommit ) {
LOG.debug( "re-enabling autocommit" );
managedConnection.setAutoCommit( true );
}
managedConnection = null;
}
catch ( Exception e ) {
LOG.debug( "Could not toggle autocommit", e );
}
}
This should not be a problem normally, because afaik the transaction should have ended after the rollback. But even more, if I issue a commit after a rollback it should not be committing any changes if there were no changes between the rollback and the commit, from the javadoc on commit:
Makes all changes made since the previous commit/rollback permanent
and releases any database locks currently held by this Connection
object. This method should be used only when auto-commit mode has been
disabled.
In this case there were no changes between rollback and commit, since the commit (triggered indirectly by re-setting autocommit) happens only a few statements later.
A work around seems to be to disable autocommit. This will avoid restoring autocommit (since it was not enabled in the first place) and thus prevent the commit from happening. You can do this by manipulating the id for the embedded datasource bean. The id is not only used for the identification of the datasource, but also for the databasename:
<jdbc:embedded-database id="dataSource;AUTOCOMMIT=OFF" type="H2"/>
This will create a database with name "dataSource". The extra parameter will be interpreted by H2. Spring will also create a bean with name "dataSource;AUTOCOMMIT=OFF"". If you depend on the bean names for injection, you can create an alias to make it cleaner:
<alias name="dataSource;AUTOCOMMIT=OFF" alias="dataSource"/>
(there isn't a cleaner way to manipulate the embedded-database namespace config, I wish Spring team would have made this a bit better configurable)
Note: disabling the autocommit via the script (<jdbc:script location="...") might not work, since there is no guarantee that the same connection will be re-used for your test.
Note: this is not a real fix but merely a workaround. There is still something wrong that cause the data to be committed after a rollback occured.
----EDIT----
After searching I found out the real problem. If you are using HibernateTransactionManager (as I was doing) and you use your database via the SessionFactory (Hibernate) and directly via the DataSource (plain JDBC), you should pass both the SessionFactory and the DataSource to the HibernateTransactionManager. From the Javadoc:
Note: To be able to register a DataSource's Connection for plain JDBC code, this instance >needs to be aware of the DataSource (setDataSource(javax.sql.DataSource)). The given >DataSource should obviously match the one used by the given SessionFactory.
So eventually I did this:
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
And everything worked for me.
Note: the same goes for JpaTransactionManager! If you both use the EntityManager and perform raw JDBC access using the DataSource, you should supply the DataSource separately next to he EMF. Also don't forget to use DataSourecUtils to obtain a connection (or JDBCTemplate which uses DataSourceUtils internally to obtain the connection)
----EDIT----
Aight, while the above did solve my problem, it is not the real cause after all :)
In normal cases when using Spring's LocalSessionFactoryBean, setting the datasource will have no effect since it's done for you.
If the SessionFactory was configured with LocalDataSourceConnectionProvider, i.e. by Spring's LocalSessionFactoryBean with a specified "dataSource", the DataSource will be auto-detected: You can still explicitly specify the DataSource, but you don't need to in this case.
In my case the problem was that we created a caching factory bean that extended LocalSessionFactoryBean. We only use this during testing to avoid booting the SessionFactory multiple times. As told before, Spring test support does boot multiple application contexts if the resource key is different. This caching mechanism mitigates the overhead completely and ensures only 1 SF is loaded.
This means that the same SessionFactory is returned for different booted application contexts. Also, the datasource passed to the SF will be the datasource from the first context that booted the SF. This is all fine, but the DataSource itself is a new "object" for each new application context. This creates a discrepancy:
The transaction is started by the HibernateTransactionManager. The datasource used for transaction synchronization is obtained from the SessionFactory (so again: the cached SessionFactory with the DataSource instance from the application context the SessionFactory was initially loaded from). When using the DataSource in your test (or production code) directly, you'll be using the instance belonging to the app context active at that point. This instance does not match the instance used for the transaction synchronization (extracted from the SF). This result into problems as the connection obtained will not be properly participating in the transaction.
By explicitly setting the datasource on the transactionmanager this appeared to be solved since the post initialization will not obtain the datasource from the SF but use the injected one instead. The appropriate way for me was to adjust the caching mechanism and replace the datasource in the cached SF with the one from the current appcontext each time the SF was returned from cache.
Conclusion: you can ignore my post:) as long as you're using HibernateTransactionManager or JtaTransactionManager in combination with some kind of Spring support factory bean for the SF or EM you should be fine, even when mixing vanilla JDBC with Hibernate. In the latter case don't forget to obtain connections via DataSourceUtils (or use JDBCTemplate).
Try this:
remove the org.springframework.jdbc.datasource.DataSourceTransactionManager
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
and replace it with the org.springframework.orm.jpa.JpaTransactionManager
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
or you inject an EntityManagerFactory instead ...
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
you need an EntityManagerFactory then, like the following
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
You haven't shown all the pieces to the puzzle. My guess at this point would be that your ApplicationSubmissionInfoDao is transactional and is committing on its own, though I'd think that would conflict with the test transactions if everything were configured properly. To get more of an answer, ask a more complete question. The best thing would be to post an SSCCE.
Thanks Ryan
The test code is something like this.
#Test
#Rollback(true)
public void shouldHave5ApplicationSubmissionInfo() {
for (int i = 0; i < 5; i++) {
hibernateTemplate.saveOrUpdate(new ApplicationSubmissionInfoBuilder()
.with(NOT_PROCESSED)
.build());
}
assertThat(repo.retrieveAll(), hasSize(5));
}
#Test
#Rollback(true)
public void shouldHave5ApplicationSubmissionInfoAgainButHas10() {
for (int i = 0; i < 5; i++) {
hibernateTemplate.saveOrUpdate(new ApplicationSubmissionInfoBuilder()
.with(NOT_PROCESSED)
.build());
}
assertThat(repo.retrieveAll(), hasSize(5));
}
I figure out that embedded DB define using jdbc:embedded-database don't have transaction support. When I used commons DBCP to define the datasource and set default auto commit to false, it worked.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" scope="singleton">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/snappDb"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
<property name="defaultAutoCommit" value="false" />
<property name="connectionInitSqls" value=""/>
</bean>
None of the above worked for me!
However, the stack i am using is [spring-test 4.2.6.RELEASE, spring-core 4.2.6.RELEASE, spring-data 1.10.1.RELEASE]
The thing is, using any unit test class annotated with [SpringJUnit4ClassRunner.class] will result in an auto-rollback functionality by spring library design
check ***org.springframework.test.context.transaction.TransactionalTestExecutionListener*** >> ***isDefaultRollback***
To overcome this behavior just annotate the unit test class with
#Rollback(value = false)
I think I've read every question and answer on Spring and autowiring a servlet, both here and at springsource.org, and I still can't get it working.
All I want to do is have the datasource automatically set in my servlets. I understand that the container creates the servlet and not Spring.
Here is code from my test servlet:
package mypackage.servlets;
imports go here...
#Service
public class TestServlet extends HttpServlet
{
private JdbcTemplate _jt;
#Autowired
public void setDataSource(DataSource dataSource)
{
_jt = new JdbcTemplate(dataSource);
}
etc etc
In my applicationContext.xml I have:
<context:annotation-config />
<context:component-scan base-package="mypackage.servlets />
<import resource="datasource.xml" />
and in my datasource.xml:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
If I can't get this working I'll just use WebApplicationContextUtils in the servlet's init method but I'd really like to make this work after all the reading I've been doing.
I'm using Spring 3, Java 1.6.
Thanks,
Paul
You need to replace your Servlets by Spring MVC contollers. Because Spring will not inject anything the classes (servlets) created by someone else then Spring itselfe (except #Configurable).
(To get an very simple example, take a look at the STS Spring Template Project: MVC).
What I wanted to do was get a DataSource reference in my Servlet for free, i.e. not calling a static getDatasource method on some class.
Here's what I learned and how I got it working:
Servlets cannot be configured or autowired by Spring. Servlets are created before Spring's app context is loaded. See issue SPR-7801: https://jira.springsource.org/browse/SPR-7801
What I did was create a DataSource in my applicationContext.xml and export that as a property:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="myDatasource">
<ref bean="dataSource"/>
</entry>
</map>
</property>
</bean>
In my servlet's init method I read the property:
public void init(ServletConfig config)
{
Object obj = config.getServletContext().getAttribute("myDatasource");
setDataSource((DataSource)obj);
}
public void setDataSource(DataSource datasource)
{
// do something here with datasource, like
// store it or make a JdbcTemplate out of it
}
If I'd been using DAOs instead of hitting the database from the servlets it would have been easy to wire them up for #Autowired by marking them #Configurable, and also be able to use #Transactional and other Spring goodies.