I am implementing the hazelcast map store for persistence. But could not autowired spring beans(DataSource below) into the hazelcast mapstore object (meaning hazelcast not getting the map store object from spring beans). I read that hazelcast supports spring DI. What am i missing ? Below is my partial map store code
If I get the bean from context using get bean like below
MySQLStore store = (MySQLStore)context.getBean(MySQLStore.class);
I get mysql store with the datasource dependency injected. So this should be an issue with Hazelcast not getting beans from spring. Am I missing any configuration in hazelcast map store.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;
import com.hazelcast.core.MapStore;
#Component
public class MySQLStore implements MapStore<String, ScripDetails> {
#Autowired
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public ScripDetails load(String arg0) {
System.out.println("loading data from store");
String sql = "SELECT * FROM DATA";
Connection conn = null;
Below is the spring-beans xml with has the datasource and component scan which includes the MySQLStore package.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.tlab"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://host:port/DB" />
<property name="username" value="username" />
<property name="password" value="password" />
</bean>
</beans>
Hazelcast xml below
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast
xsi:schemaLocation="http://www.hazelcast.com/schema/config
http://www.hazelcast.com/schema/config/hazelcast-config-3.0.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<network>
<join><multicast enabled="true"/></join>
</network>
<map name="scrips">
<backup-count>1</backup-count>
<time-to-live-seconds>0</time-to-live-seconds>
<max-idle-seconds>0</max-idle-seconds>
<eviction-policy>LRU</eviction-policy>
<eviction-percentage>25</eviction-percentage>
<merge-policy>hz.ADD_NEW_ENTRY</merge-policy>
<map-store enabled="true">
<class-name>com.tlab.MySQLStore</class-name>
</map-store>
</map>
</hazelcast>
In your example, Hazelcast instance is not a Spring bean. You need to configure Hazelcast instance using Spring. You can find documentation here and examples
I hope that it helps.
Thank you
Related
I am trying DI with autowiring and I came across #Qualifier annotation annd tried the following code:
Car.java
package beans;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Qualifier;
public class Car {
#Autowired
#Qualifier("e1")
private Engine engine;
// no need to have setter or constructor
public void showData(){
System.out.println("Engine Model Year : "+engine.getModelyear());
}
}
Engine.java
package beans;
public class Engine {
private String modelyear;
public void setModelyear(String modelyear) {
this.modelyear = modelyear;
}
public String getModelyear() {
return modelyear;
}
}
Spring.xml
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<!-- activate autowire annotation -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="e1" class="beans.Engine">
<property name="modelyear" value="2017"/>
</bean>
<bean id="e2" class="beans.Engine">
<property name="modelyear" value="2018"/>
</bean>
<bean id="c" class="beans.Car">
</bean>
</beans>
Main.java
package MainClass;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import beans.Car;
public class AutoAnno_Main {
public static void main(String[] args) {
ApplicationContext ap=new ClassPathXmlApplicationContext("resources/spring.xml");
Car c=(Car)ap.getBean("c");
c.showData();
}
}
And the error I am getting is:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'beans.Engine' available: expected single matching bean but found 2: e1,e2
what's wrong in this I think the syntax is correct is there any problem with version
I am using eclipse Oxygen
You just need to add <context:annotation-config /> in your spring.xml. Certain spring annotations are not activated unless this is added. In your case, spring is not reading the #Qualifier annotation without the <context:annotation-config />.
I have tested by adding this and it seems to work.
Update:
Your spring xml needs to have the spring schema for that to detect <context:annotation-config>. Your final xml looks like this.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- activate autowire annotation -->
<context:annotation-config />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="e1" class="beans.Engine">
<property name="modelyear" value="2017"/>
</bean>
<bean id="e2" class="beans.Engine">
<property name="modelyear" value="2018"/>
</bean>
<bean id="c" class="beans.Car">
</bean>
</beans>
I have a very strange problem and looking for the solution here. I spent significant amount of time by reading articles and other questions on SO however no luck.
I am using #Transactional annotation in my sample application of Spring 3 and hibernate 3 as shown below. In the last line of method I am explicitly throwing NullPointerException as shown below.
package com.mkyong.common;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mkyong.stock.bo.StockBo;
import com.mkyong.stock.model.Stock;
#Service("stockService")
public class StockServiceImpl {
final static Logger logger = Logger.getLogger(StockServiceImpl.class);
#Transactional(rollbackFor ={ NullPointerException.class} )
public void createNewStock(StockBo stockBo) {
/** insert **/
Stock stock = new Stock();
String code = "xee";
stock.setStockCode(code);
stock.setStockName(code);
stockBo.save(stock);
logger.debug("#################### After Save ##########################");
throw new NullPointerException();
}
}
However this transaction is not rolling back and always commiting to database even though NullPointerException is thrown.
Below is my application context file
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<!-- Database Configuration -->
<import resource="../database/DataSource.xml" />
<import resource="../database/Hibernate.xml" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Auto scan the components -->
<context:component-scan base-package="com.mkyong.stock" />
<bean
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
Judging from your configuration you are using Hibernate, however you are using the transaction manager that is solely for plain JDBC usage.
To have proper transaction management you must use the PlatformTransactionManager which belongs to your persistence technology. In your case you should use the HibernateTransactionManager instead of the DataSourceTransactionManager.
<bean id="transactionManager" class="org.springframework.hibernate5.HibernateTransactionManager>
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Note: This is for Hibernate5 (see the package name) use the one that fits your version of hibernate.
Make sure that the class that contains the method is in the application context not in the web application context (simple: the parent class of the method should be a service, repository etc. not a controller).
Also check that you import the #Transactional annotation from the right spring package.
Using only #Transactional annotation is enough. You dont require rollbackFor attribute to rollback on NullPointerException. Transactions with #Transactional annotation are defaulted to RuntimeException.
You should add rollbackFor only if you want to rollback on Checked Exceptions.
With SpringJUnit4ClassRunner, JUnit 4 and Spring Test I wrote unit test for Service which uses Spring Data JPA Repository and embedded HSQL database:
#Ignore
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath*:unitTestFullConfig.xml")
public class InMemoryDBFullTestBaseClass {
}
public final class ActorServiceImplTest extends InMemoryDBFullTestBaseClass {
#Inject
private ActorService service;
#Test
public final void saveActor () throws Exception {
service.save(new ActorDTO(null, "testName", "testSurname", new Date(), Collections.emptyList()));
assertEquals(1, service.getAll().size());
}
}
I run test with required javaagent option on VM, with config:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Configure the data source bean -->
<jdbc:embedded-database id="dataSource" type="HSQL">
</jdbc:embedded-database>
<!-- Enable annotation driven transaction management -->
<tx:annotation-driven/>
<mvc:annotation-driven/>
<context:component-scan base-package="beans"/>
<!-- Create default configuration for Hibernate -->
<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<!-- Set JPA properties -->
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="javax.persistence.schema-generation.database.action">none</prop>
<prop key="hibernate.ejb.use_class_enhancer">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<!-- Set base package of your entities -->
<property name="packagesToScan" value="models"/>
<!-- Set share cache mode -->
<property name="sharedCacheMode" value="ENABLE_SELECTIVE"/>
<!-- Set validation mode -->
<property name="validationMode" value="NONE"/>
<property name="persistenceUnitName" value="testJPA" />
</bean>
<!-- Configure the transaction manager bean -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--
Configure Spring Data JPA and set the base package of the
repository interfaces
-->
<jpa:repositories base-package="beans.repositories"/>
</beans>
But I got:
Error creating bean with name 'entityManagerFactory' defined in URL [file:/E:/workspace/film-site/out/test/main/unitTestFullConfig.xml]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.hibernate.engine.spi.SessionFactoryImplementor.getProperties()Ljava/util/Properties;
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean...
The only difference between test config and applicationContext.xml (which works for Tomcat app) is embedded database used in test, but even if I use dataSource from project:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver"/>
<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/film-site"/>
<property name="user" value="postgres"/>
<property name="password" value="postgres"/>
<property name="maxPoolSize" value="10"/>
<property name="maxStatements" value="0"/>
<property name="minPoolSize" value="5"/>
</bean>
I still face the same issue (project works normally). Also, i don't think problem is that I don't have hibernate.properties file, cause I asked about it here: Spring Data configuration - hibernate.properties not found. I use Spring 4.3.2.RELEASE, with Hibernate Core 5.2.0.Final, Hibernate Entity Manager 5.1.0.Final, Spring Data 1.10.2.RELEASE JPA, Spring Data Commons 1.12.2.RELEASE and Spring Data Commons Core 1.4.1.RELEASE. I will be happy if anybody would help me - thank you in advance.
UPDATE: I changed jpaProperties to jpaPropertyMap for entityManagerFactory:
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
<entry key="javax.persistence.schema-generation.database.action" value="none" />
<entry key="hibernate.ejb.use_class_enhancer" value="true" />
<entry key="hibernate.hbm2ddl.auto" value="create" />
<entry key="hibernate.show_sql" value="true" />
</map>
</property>
And comment dependency for hibernate-entitymanager, but it still does not work. Also I have the same issue when I switch to Hibernate 5.1
UPDATE 2: I created a Java config version, maybe it will help someone to see where I made a mistake:
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
public class HibernateConfig {
#Bean
public DataSource dataSource () {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build();
}
// Create default configuration for Hibernate
#Bean
public JpaVendorAdapter jpaVendorAdapter () {
return new HibernateJpaVendorAdapter();
}
// Configure the entity manager factory bean
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory () {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(jpaVendorAdapter());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
factory.setJpaPropertyMap(createJpaProperties());
factory.setPackagesToScan("models");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setPersistenceUnitName("testJPA");
return factory;
}
#Bean
public JpaTransactionManager transactionManager () {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory((EntityManagerFactory) entityManagerFactory());
return transactionManager;
}
private Map<String, ?> createJpaProperties () {
Map<String, Object> propertyMap = new HashMap();
propertyMap.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
propertyMap.put("javax.persistence.schema-generation.database.action", "none");
propertyMap.put("hibernate.ejb.use_class_enhancer", true);
propertyMap.put("hibernate.hbm2ddl.auto", "create");
propertyMap.put("hibernate.show_sql", true);
return propertyMap;
}
}
UPDATE 2016-10-04: I created Github repository which shows problem, there you will see that app itself works quite well (just add Actor in form inside index,jsp file, but it does not work for test). P.S. I deploy war:exploded to Tomcat 8.0.24 local instance from Intellij IDEA ("Run" button).
Using Hibernate Core 5.2 and Hibernate EntityManager 5.1 is very likely to cause the issue here. 5.2 moved the EntityManager implementation into the core module so that you end up with 2 JPA implementations on the classpath which probably causes Spring framework to fail to detect the Hibernate version to bootstrap.
Make sure you either use Hibernate 5.1 and refer to the hibernate-entitymanager artifact or 5.2 and only pull in hiberante-core.
I checked your example on GitHub, and Unit-Test has worked for me after some code modifications. But first I wanted to say that it was little bit another Exception for me in the beginning. It was like follows:
Caused by: java.lang.ClassCastException: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean$$EnhancerBySpringCGLIB$$fedd095f cannot be cast to javax.persistence.EntityManagerFactory
JpaTransactionManager configuration in HibernateConfig need to be changed to fix this problem like follows:
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
Unlike the original config version in this case EntityManagerFactory is injected as Spring managed bean, instead of separate instance that created with HibernateConfig.entityManagerFactory() method.
Also other changes have been made, but they are not directly related to the Question subject. I can provide them too if you want.
While fixing this bug in hibernate community the signature of SessionFactoryImplementor.getProperties() was changed in 5.2 to return Map rather than Properties. reference
Hence, you should use Map.
You are using hibernate 5.2.0 Final, In the new version of hibernate 5.2.3 Final the community have consolidated few hibernate-entitymanager issues. download link. Please try with this version.
Suggestion:
1) Use the below hibernate-core and hibernate-entitymanager versions instead of 5.2.0.Final and 5.1.0.Final versions respectively.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.1.Final</version>
</dependency>
2) Revert to the Hibernate 5.1.x release (I guess you should have no issues with this.)
3) If the first & second suggestions didn't worked then move on to Release 6.0.1.GA, it is compatible with Hibernate 5.2. community discussion
4) Instead of below in config (just for the sake of trial and error method.)
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<!-- Set JPA properties -->
<property name="jpaPropertyMap">
<map>
...
</map>
</property>
...
</bean>
Use this code:
<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<property name="jpaPropertyMap" ref="jpaPropertyMap" />
...
</bean>
<util:map id="jpaPropertyMap" map-class="java.util.TreeMap"> <!-- OR <util:map id="jpaPropertyMap"> OR <util:map id="jpaPropertyMap" map-class="java.util.HashMap">-->
<entry key="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
...
<entry key="hibernate.show_sql" value="true"/>
...
</util:map>
It can be out of topic but your sample project can be also improved with some additional changes to prevent problems in the future. This improvements below:
Explicitly define versions and configuration for all Maven plugins you are using. At least it is Maven Compiler Plugin:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.source}</source>
<target>${java.target}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
Follow maven project structure conventions. In your case test related source code must be placed in src/test/java folder instead of src/main/test:
my-app
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- mycompany
| `-- app
| `-- App.java
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
It is good practise to define your own package name instead of just beans or config. For example it can be com.pneumokok.mvc, com.pneumokok.service, com.pneumokok.test and com.pneumokok.model. It can help you with component scan using base package name.
As you rightly pointed out it is necessary to add javax.servlet-api dependency, but it is important to define provided scope
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
Last but not least Use separate Spring context definition for each Application layer and environment. In your example several context definitions can be used:
src/main/resources/com/pneumokok/service/applicationContext.xml
Spring context configuration for Service layer
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pneumokok.service"/>
<!-- Enable annotation driven transaction management -->
<tx:annotation-driven/>
...
</beans>
src/main/resources/com/pneumokok/mvc/applicationContext.xml
Spring context configuration for MVC layer
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pneumokok.mvc"/>
<!-- Enable annotation driven transaction management -->
<mvc:annotation-driven/>
...
</beans>
src/main/resources/com/pneumokok/applicationContext.xml
Spring context configuration for Servlet container environment
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="com/pneumokok/service/applicationContext.xml"/>
<import resource="com/pneumokok/mvc/applicationContext.xml"/>
<import resource="com/pneumokok/dao/applicationContext.xml"/>
<bean name="dataSource">
//Servlet container DataSource configuration
</bean>
...
</beans>
src/main/resources/com/pneumokok/test/applicationContext.xml
Spring context configuration for Test environment
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="com/pneumokok/service/applicationContext.xml"/>
<import resource="com/pneumokok/mvc/applicationContext.xml"/>
<import resource="com/pneumokok/dao/applicationContext.xml"/>
<bean name="dataSource">
//Test DataSource configuration
</bean>
...
</beans>
I can't get #Inject to work properly. I'm trying to inject a bean from xml using the #Inject annotation, but I get the error message
"java.lang.IllegalArgumentException: 'dataSource' or 'jdbcTemplate' is required".
I have also been trying in combination with #Qualifier("dataSource"), but wherever I put the #Qualifier it says "The annotation #Qualifier is disallowed for this location".
I have been reading loads of documentation on #Inject and I can't seem to find anything that mentions any special treatment of beans declared in xml.
However, I'm guessing Spring is trying to create the FooDaoImpl bean before scanning the dataSourceBean.
How would I go about using #Inject to inject the dataSource bean declared in the xml file?
Is it even possible, using #Inject?
FooDaoImpl.java
#Repository
public class FooDaoImpl extends NamedParameterJdbcDaoSupport implements FooDao {
#Inject
private DataSource dataSource;
DSLContext create = DSL.using(dataSource, SQLDialect.DB2);
}
Spring-Module.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
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="com.example.foobar" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.ibm.db2.jcc.DB2Driver" />
<property name="jdbcUrl" value="jdbc:db2://localhost:50000/BLABLA" />
<property name="user" value="PAPAYA" />
<property name="password" value="COCONUT" />
</bean>
Cheers!
This works fine in Spring. I use the #Autowired annotation, not #Inject.
I managed to use #Inject to inject the dataSource to my Dao. I used a #PostConstruct to achieve this, like so:
#Inject
private DataSource dataSource;
#PostConstruct
private void initialize(){
setDataSource(dataSource);
}
DSLContext create = DSL.using(dataSource, SQLDialect.DB2);
I'm sure there is a better, or "cleaner" way of achieving this, but none that I could find.
Thanks for your suggestions!
To get rid of the The annotation #Qualifier is disallowed for this location message you have to use the annotation #interface.
I am using Unitils with Spring for unit testing. I've configured Spring with datasource using a properties file.
My question is how can I use the same datasource or the same properties for Unitils?
Unitils expects a file in the classpath unitils.properties with database configuration parameters like url, user, password and driver.
I've tried to configure Unitils using the properties used in the Spring configuration as below but it is not working.
database.driverClassName=${jdbc.driver.class}
Thanks,
Adi
One potential solution... You could have your Spring configuration read its datasource parameters from the unitils.properties, instead of the other way around. Probably not ideal.
I believe unitils is using spring under the covers, so you might also try adding your datasource context in your unitils tests by using #SpringApplicationContext. If you could figure out the name of the datasource bean setup by unitils when it starts up, you could override it in your context (assuming the unitils datasource bean is created before the other spring beans are which may/may not be true.)
e.g.
#SpringApplicationContext({"correctDataSourceContext.xml"})
EDIT: Another option that will definitely work: https://stackoverflow.com/a/6561782/411229
Basically instantiate Unitils yourself and set the properties manually.
Ryan answer is correct and helpful as well though I've used different approach.
I extended the class PropertiesDataSourceFactory ro override the methods as follows:
public class UnitilsDataSourceFactory extends PropertiesDataSourceFactory {
#Override
public void init(Properties configuration) {
try {
String[] configFiles = new String[] { "applicationContext-test.xml" };
BeanFactory factory = new ClassPathXmlApplicationContext(configFiles);
SystemPropertiesReader systemPropertiesReader = (SystemPropertiesReader) factory.getBean("systemPropertiesReader");
Properties loadProperties = systemPropertiesReader.loadProperties();
super.init(loadProperties);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public DataSource createDataSource() {
DataSource dataSource = super.createDataSource();
return dataSource;
}
}
and also wrote a SystemPropertiesReader as:
public class SystemPropertiesReader {
private Collection<Resource> resources;
public void setResources(final Collection<Resource> resources) {
this.resources = resources;
}
public void setResource(final Resource resource) {
resources = Collections.singleton(resource);
}
#PostConstruct
public Properties loadProperties() throws Exception {
final Properties systemProperties = System.getProperties();
for (final Resource resource : resources) {
final InputStream inputStream = resource.getInputStream();
try {
systemProperties.load(inputStream);
} finally {
//
}
}
return systemProperties;
}
}
and added a bean with the properties file:
<bean id="systemPropertiesReader" class="uk.co.friendslife.eventmanager.domain.dao.SystemPropertiesReader">
<property name="resource">
<value>classpath:/META-INF/em/config/eventmanager_${database_name_lower}.properties</value>
</property>
</bean>
add the following to unitils.properties:
org.unitils.database.config.DataSourceFactory.implClassName=x.y.UnitilsDataSourceFactory
Just want to add some idea and im not sure if it is a best practice or not so correct me if theres something wrong.
MYPROJECT
-src
--TestPackage
---BaseServiceTest.class
---BlogspotServiceTest.class
--hibernate.cfg.xml
-web
--WEB-INF
---blogspot-servlet-test.xml
---jdbc-test.properties
in my case I used my blogspot-servlet-test.xml to call or to create the datasource
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
.... some bean configuration
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="file:web/WEB-INF/jdbc.properties"/>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- DAO'S -->
<bean id="blogspotDAO" class="package.BlogspotDAOImpl"/>
<!-- SERVICES -->
<bean id="blogspotService" class="package.BlogspotServiceImpl"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
MY jdbc-test.properties file
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.dialect=org.hibernate.dialect.MySQL5Dialect
jdbc.databaseurl=jdbc:mysql://127.0.0.1:3306/dbspringminiblogtest
jdbc.username=root
jdbc.password=
For hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd//hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping class="somePackage.entity.Author"/>
<!-- Other Entity Class to be mapped -->
</session-factory>
</hibernate-configuration>
and i created BaseClass for me to lessen creating of multiple #SpringApplicationContext annotation and it is also use to configure common configuration needed in testing other class, just extends it.
#SpringApplicationContext({"file:web/WEB-INF/blogspot-servlet-test.xml"})
public class BaseServiceTest extends UnitilsJUnit4 {
}
i used #SpringApplicationContext to load the datasource and other bean configurations on my BaseClass and this is how i implement it.
Below : see Spring-Unitils Tutorial
for more details
public class BlogspotServiceTest extends BaseServiceTest{
#Mock
#InjectInto(property = "blogspotDAO")
#SpringBean("blogspotDAO")
private BlogspotDAO blogspotDAOMock;
#TestedObject
#SpringBean("blogspotService")
private BlogspotService blogspotServiceMock;
#Test
public void testAddBlogSpot() {
assertNotNull("BlogspotService Not null",blogspotServiceMock);
}
}
NOTE: please create unitils.properties and unitils-local.properties inside TestPackage to be able to run the program.
For #SpringBean explanation and other annotation please read :
Unitils-EasyMock