I have a web project working with Spring3 and Hibernate4 and now I want to test the DAOs without using the xml files. To do so I have created a class that creates a LocalSessionFactoryBean with the data contained in the application's xml file and a simple test class.
However, the sessionFactory returned by localSessionFactoryBean.getObject() is null. I have been looking at some examples like this and they have the same problem when I modify them to run without Spring. Do you have any idea?
This is the code that prepares the sessionFactory:
#Configuration
#Transactional
#EnableTransactionManagement
#ComponentScan({ "com.company" })
public class HibernateInitializator {
public SessionFactory getSessionFactory() {
Properties hibernateProperties = getHibernateProperties();
DataSource dataSource = getDatasourceConfiguration();
LocalSessionFactoryBean localSessionFactoryBean = generateSessionFactoryBean(new String[] { "com.company" },
dataSource, hibernateProperties);
SessionFactory sessionFactory = localSessionFactoryBean.getObject();
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return sessionFactory;
}
private DataSource getDatasourceConfiguration() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/dbName");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
private static LocalSessionFactoryBean generateSessionFactoryBean(String[] basePackage, DataSource dataSource,
Properties hibernateProperties) {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan(basePackage);
localSessionFactoryBean.setHibernateProperties(hibernateProperties);
return localSessionFactoryBean;
}
private static Properties getHibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
hibernateProperties.put("hibernate.show_sql", false);
hibernateProperties.put("hibernate.generate_statistics", false);
hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
hibernateProperties.put("hibernate.use_sql_comments", false);
return hibernateProperties;
}
}
And this is a simple test class that uses it:
public class GenericDAOHibernateTest {
private GenericDAOHibernate dao;
#BeforeTest
private void testInitialization() {
dao = new GenericDAO();
HibernateInitializator initializator = new HibernateInitializator();
SessionFactory sessionFactory = initializator.getSessionFactory();
dao.setSessionFactory(sessionFactory);
}
#Test(description = "Checks that returns the user list ")
public void shouldReturnsUserList() throws SQLException, Exception {
List<Object[]> openResultSetList = dao.doSomeOperation();
...
}
}
Try adding this line
localSessionFactoryBean.afterPropertiesSet();
in the method after the properties of LocalSessionFactoryInstance has been set. Your method will be as
private static LocalSessionFactoryBean generateSessionFactoryBean(String[] basePackage, DataSource dataSource,
Properties hibernateProperties) {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan(basePackage);
localSessionFactoryBean.setHibernateProperties(hibernateProperties);
// Added the below line
localSessionFactoryBean.afterPropertiesSet();
return localSessionFactoryBean;
}
This link may add more insight to the problem.
As per Documentation,
public void afterPropertiesSet() throws IOException
Invoked by a BeanFactory after it has set all bean properties supplied (and satisfied BeanFactoryAware and ApplicationContextAware).
This method allows the bean instance to perform initialization only possible when all bean properties have been set and to throw an exception in the event of misconfiguration.
In your case, I think you need to call it in your code manually.
I asked how to do it without an xml file and the solution proposed by #Octopus fixed the problem I had. However, after fixing that arised other errors with transactions... so I decided to do it in a different way that doesn't interfere with the application.
To do so I created a simplified version of the xml file in /src/test/resources removing many configurations like c3p0's one and hardcoding the values so that the tests won't fail if the application's properties files are changed.
This is the content of the xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" ...>
<!-- Spring DataSource -->
<bean id="testDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/testDatabase" />
<property name="username" value="testUsername" />
<property name="password" value="testPassword" />
</bean>
<!-- Hibernate 4 SessionFactory -->
<bean id="testSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" p:dataSource-ref="testDataSource">
<property name="packagesToScan" value="com.company" />
<property name="hibernateProperties">
<props>
<!-- Hibernate basic configuration -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.generate_statistics">false</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.use_sql_comments">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="testDataSource" />
<property name="sessionFactory" ref="testSessionFactory" />
</bean>
<context:annotation-config />
<tx:annotation-driven />
<!-- Initialization of DAOs -->
<bean id="userDao" name="userDao" class="com.company.dao.UserDAOHibernate" autowire="byName"/>
...
</beans>
In the test class I have used JUnit because I haven't managed to do it with TestNG although I tried extending AbstractTestNGSpringContextTests. I would recommend using JUnit 4.4 version instead of newer versions because it has classes like AssumptionViolatedException that are used by spring-test 2.5.
This is the code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/test/resources/application-config-tests.xml" })
public class SimpleUserDAOTest {
#Autowired
private UserDAO dao;
#Autowired
private SessionFactory sessionFactory;
#Before
public void setUp() throws Exception {
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(sessionFactory.openSession()));
}
#After
public void tearDown() throws Exception {
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
#Test
public void shouldReturnActiveUsers() {
List<User> userList = dao.getActiveUsers();
...
}
}
I hope this helps you in your future developments
When you are using Spring 3+ and Hibernate 4, I would suggest you a better way, define everything as annotated Spring Configurations and this work smoothly.
Below is the detailed code, just copy and paste in your project and you can Autowire SessionFactory anywhere in the DAO layer or any spring component :)
#Configuration
#Import(HibernateConfig.class)
#EnableWebMvc
#ComponentScan(basePackages = "com.mypackages")
public class WebConfig extends WebMvcConfigurationSupport {
#Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.parameterName("mediaType").ignoreAcceptHeader(true)defaultContentType(MediaType.APPLICATION_JSON).mediaType("xml", MediaType.APPLICATION_XML).mediaType("json", MediaType.APPLICATION_JSON);
}
#Bean(name = "validator")
public Validator validator() {
return new LocalValidatorFactoryBean();
}
}
Then define a HibernateConfig as below:
#Configuration
#EnableTransactionManagement
#EnableAspectJAutoProxy
#PropertySource({"classpath:app.properties"})
#ComponentScan(basePackages = "com.mypackages")
public class HibernateConfig {
#Value(Constants.HIBERNATE_DIALECT)
private String hibernateDialect;
#Autowired
private DataSource dataSource;
#Bean(name = "appProperty")
public static PropertySourcesPlaceholderConfigurer appProperty() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory() throws Exception {
Properties properties = new Properties();
properties.put(Constants.HIBERNATE_DIALECT_PROPERTY,
hibernateDialect);
properties.put(Constants.HIBERNATE_SHOW_SQL_PROPERTY,
"false");
properties
.put(Constants.HIBERNATE_CURRENT_SESSION_CONTEXT_CLASS_PROPERTY,
"thread");
properties
.put("dynamic-update","true");
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setPackagesToScan(new String[] { Constants.DOMAIN_MODEL_PACKAGE });
factory.setDataSource(dataSource);
factory.setHibernateProperties(properties);
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager() throws Exception {
return new HibernateTransactionManager(getSessionFactory());
}
}
Then define a JndiConfig as below:
#Configuration
public class JndiConfig {
#Value(Constants.DRIVER_CLASS)
private String driverClassName;
#Value(Constants.DATABASE_URL)
private String databaseURL;
#Value(Constants.USER_NAME)
private String username;
#Value(Constants.PASSWORD)
private String password;
#Value(Constants.MAX_ACTIVE)
private int maxActive;
#Value(Constants.MAX_IDLE)
private int maxIdle;
#Value(Constants.MAX_WAIT)
private long maxWait;
#Value(Constants.MIN_IDLE)
private int minIdle;
#Value(Constants.INITIAL_SIZE)
private int initialSize;
#Value(Constants.TIME_BETWEEN_EVICTION)
private long timeBetweenEvictionRunsMillis;
#Bean(name = "dataSource")
public DataSource dataSource() throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(databaseURL);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);
dataSource.setValidationQuery("SELECT 1");
dataSource.setMaxActive(maxActive);
dataSource.setMaxIdle(maxIdle);
dataSource.setMaxWait(maxWait);
dataSource.setMinIdle(minIdle);
dataSource
.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
return dataSource;
}
}
Related
I have a Junit which somewhere down the line invokes a DaoImpl and this DaoImpl has a datasource attribute. Since this DaoImpl extends NamedParameterJdbcDaoSupport where setDataSource is marked final I cannot autowire a setter for this. My code (only when) invoked from a Junit fails with NPE at dataSource.getConnection() due to the dataSource attribute being null.
Here are the two things that I tried that works,
1> if I try below snippet and use this second attribute dataSource2.getConnection() in the called method it works just fine.
#Autowired
private DataSource dataSource;
#Autowired
private DataSource dataSource2;
#Autowired
public void setDataSource2(DataSource dataSource2){
this.dataSource2=dataSource2;
}
2> If I call parent getter directly while obtaining a connection within the called method it works just fine.
getDataSource.getConnection()
But it always fails if I use dataSource.getConnection() for below variations that I have tried,
#Autowired
private DataSource dataSource;
#Autowired
public void setDs(DataSource dataSource) {
this.dataSource = dataSource
}
OR
#Autowired
private DataSource dataSource;
#Autowired
private DataSource dataSource2;
#Autowired
public void setDataSource2(DataSource dataSource) {
this.dataSource2 = dataSource;
this.dataSource = dataSource;
}
I have also tried suggestion provided here,
#Autowired
private DataSource dataSource;
#PostConstruct
private void initialize() {
setDataSource(dataSource);
}
and here
#Autowired
public void setDs(DataSource dataSource) {
setDataSource(dataSource);
}
I can just go use getDataSource().getConnection() but this line dataSource.getConnection() is used in many places within the application and I cannot change it everywhere, that piece of code works just fine when deployed on JBOSS application server, just not via my Junit. Much appreciated if anyone point me what is going on here for the solutions that I tried and what I could do interms of Junit to fix this without having to change functional part of the code?
My junit class and application context looks as below:
public class MyTestC extends TestCase {
private ApplicationContext context;
public void setContext(ApplicationContext context) {
this.context = context;
}
private AToolController aToolController;
public void setUp() throws NamingException {
context = new ClassPathXmlApplicationContext("classpath:MyTestC.xml");
aToolController = (AToolController) context.getBean("AToolController");
}
#Test
public final void testDoSomething() throws AppException {
//aToolController.invokeSomething();
}
}
Context xml:
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate"
scope="prototype">
<constructor-arg><ref bean="datasource"/></constructor-arg>
</bean>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:mytest.properties</value>
</property>
</bean>
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${mytest.jdbc.driverClassName}"></property>
<property name="url" value="${mytest.jdbc.url}"></property>
<property name="username" value="${mytest.jdbc.userName}"></property>
<property name="password" value="${mytest.jdbc.password}"></property>
</bean>
<bean id="ptDAO" class="com.ctl.abc.def.ghi.dao.AToolDAOImpl">
<property name="dataSource" ref="datasource"></property>
</bean>
<bean id="ptService" class="com.ctl.abc.def.ghi.service.AToolService">
<property name="ptDAO" ref="ptDAO"></property>
</bean>
<bean id="AToolController" class="com.ctl.abc.def.ghi.AToolController">
<property name="ptService" ref="ptService"></property>
</bean>
I use generally xml configuration in my Spring project under the three files:
applicationContext.xml:
This file contains the main xml configuration: component scan, annotation-config and also an include of two other xml configuration files:
applicationContext-db.xml
This file contains all database beans: dataSource, SessionFactory, ...
applicationContext-security.xml
This file contains all spring security config.
I need to use also Spring Security ACL, for this I created a configuration class:
AclMethodSecurityConfiguration.java
package com.medkhelifi.tutorials.todolist.conf;
/**
/* all imports goes here.
**/
#Configuration
#ImportResource({"classpath*:conf/applicationContext-db.xml"})
#EnableGlobalMethodSecurity (prePostEnabled = true, securedEnabled = true)
public class AclMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
DataSource dataSource;
#Bean
public MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService());
expressionHandler.setPermissionEvaluator(permissionEvaluator);
return expressionHandler;
}
#Bean
public JdbcMutableAclService aclService() {
return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
}
#Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
#Bean
public PermissionGrantingStrategy permissionGrantingStrategy() {
return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
}
#Bean
public EhCacheBasedAclCache aclCache() {
return new EhCacheBasedAclCache(
aclEhCacheFactoryBean().getObject(),
permissionGrantingStrategy(),
aclAuthorizationStrategy()
);
}
#Bean
public EhCacheFactoryBean aclEhCacheFactoryBean() {
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
ehCacheFactoryBean.setCacheName("aclCache");
return ehCacheFactoryBean;
}
#Bean
public EhCacheManagerFactoryBean aclCacheManager() {
return new EhCacheManagerFactoryBean();
}
#Bean
public LookupStrategy lookupStrategy() {
return new BasicLookupStrategy(
dataSource,
aclCache(),
aclAuthorizationStrategy(),
new ConsoleAuditLogger());
}
}
My problem is the datasource autowired into Configuration file is null, I don't kwno if i missed something.
My XMLs files are all under: src/main/resources/conf/
There is my datasource bean definition in applicationContext-db.xml
<!-- DATASOURCE -->
<bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${database.driver}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
I already used this bean into Sessionfactory bean defined in the same applicationContext-db.xml file.
PS: When I remove the extended class GlobalMethodSecurityConfiguration my datasource is defined, but I need this org.springframework.security.config.annotation.method.configurationclass to set up my Spring Security ACL configuration.
I found a way to define my datasource bean by using the BeanFactoryAware interface.
BeanFactoryAware is used to inject the BeanFactory object. This way we get access to the BeanFactory which created the object.
#EnableGlobalMethodSecurity (prePostEnabled = true, securedEnabled = true)
#Configuration
#ImportResource({"classpath:/conf/applicationContext-db.xml"})
public class AclMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration implements BeanFactoryAware {
DataSource dataSource;
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.dataSource = beanFactory.getBean("dataSource", DataSource.class);
}
// rest of code goes here
}
I read that if we use this technic that means we are doing something wrong, I'll continue searching for a proper solution.
Please rename your bean as name="dataSource"
I have this strange situation, when in test environment everything works fine, but when deployed on WildFly entityManager cannot find a transaction for some reason.
I already checked if bean is created twice - once in mvc and once in core context - but trace only appears once.
Tried pretty much every other fix for this on StackOverflow, but the problem (unlike the data i want EntityManager to save) persists.
Maybe you'd be able to spot some error in my config which i failed to find?
Hibernate java config (DataSource is initialized in another module, switching to commented DS does nothing):
#Configuration
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#EnableJpaRepositories(
entityManagerFactoryRef = "user.management.entityManagerFactory",
transactionManagerRef = "user.management.transactionManager")
public class HibernateConfiguration {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "org.postgresql.Driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "";
private static final String PROPERTY_NAME_DATABASE_URL = "jdbc:postgresql://localhost:5432/postgres";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "postgres";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
#Resource
private Environment env;
#Autowired
#Bean(name = "user.management.jpaDialect")
HibernateJpaDialect getDialect() {
return new HibernateJpaDialect();
}
#Autowired
#Bean(name = "user.management.jpaVendorAdapter")
HibernateJpaVendorAdapter getJpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.HSQLDialect");
vendorAdapter.setGenerateDdl(false);
return vendorAdapter;
}
#Autowired
#Bean(name = "user.management.impl.repository.util.DbUtil")
DbUtil getDbUtil(
#Qualifier("DataSource")
DataSource dataSource
) {
DbUtil dbUtil = new DbUtil(dataSource);
return dbUtil;
}
#Autowired
#Bean(name = "hibernate5AnnotatedSessionFactory")
LocalSessionFactoryBuilder getLocalSessionFactoryBean(
#Qualifier("DataSource")
DataSource dataSource
) {
LocalSessionFactoryBuilder localSessionFactoryBean =
new LocalSessionFactoryBuilder(dataSource);
localSessionFactoryBean.scanPackages(
"user.management.impl.repository.pojo"
);
localSessionFactoryBean.addProperties(getHibernateProperties());
localSessionFactoryBean.buildSessionFactory();
return localSessionFactoryBean;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "false");
properties.put("hibernate.current_session_context_class", "thread");
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
return properties;
}
/*#Autowired
#Bean(name = "user.management.dataSource")
public DataSource getPooledDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(PROPERTY_NAME_DATABASE_DRIVER);
dataSource.setUrl(PROPERTY_NAME_DATABASE_URL);
dataSource.setUsername(PROPERTY_NAME_DATABASE_USERNAME);
dataSource.setPassword(PROPERTY_NAME_DATABASE_PASSWORD);
return dataSource;
}*/
#Autowired
#Bean(name = "user.management.entityManagerFactory")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactory(
#Qualifier("DataSource")
DataSource dataSource
) {
System.out.println("getEntityManagerFactory()");
LocalContainerEntityManagerFactoryBean theEntityManager = new LocalContainerEntityManagerFactoryBean();
theEntityManager.setPersistenceXmlLocation("classpath:META-INF/persistence.xml");
theEntityManager.setPersistenceUnitName("umPersistenceUnit");
theEntityManager.setDataSource(dataSource);
theEntityManager.setPackagesToScan(
"user.management.impl.repository.pojo",
);
theEntityManager.setJpaVendorAdapter(getJpaVendorAdapter());
theEntityManager.setJpaDialect(getJpaDialect());
theEntityManager.afterPropertiesSet();
return theEntityManager;
}
#Autowired
#Bean(name = "user.management.jpaDialect")
public JpaDialect getJpaDialect() {
return new HibernateJpaDialect();
}
#Autowired
#Bean(name = "user.management.transactionManager")
public JpaTransactionManager getTransactionManager(
#Qualifier("user.management.entityManagerFactory")
EntityManagerFactory entityManagerFactory,
#Qualifier("DataSource")
DataSource dataSource
) {
JpaTransactionManager jpaTranstactionManager =
new JpaTransactionManager(entityManagerFactory);
jpaTranstactionManager.setDataSource(dataSource);
jpaTranstactionManager.setJpaDialect(getJpaDialect());
jpaTranstactionManager.setEntityManagerFactory(entityManagerFactory);
jpaTranstactionManager.afterPropertiesSet();
return jpaTranstactionManager;
}
//DAO Classes
#Autowired
#Bean(name = "user.management.userRepository")
public UserRepository getUserDao(
#Qualifier("cacheManager")
CacheManager cacheManager) {
return new UserDaoImpl(getIdGenerator(), cacheManager);
}
#Autowired
#Bean(name = "user.management.groupDao")
public GroupRepository getGroupDao() {
return new GroupDaoImpl(getIdGenerator());
}
#Autowired
#Bean(name = "user.management.roleDao")
public RoleRepository getRoleDao() {
return new RoleDaoImpl(getIdGenerator());
}
#Autowired
#Bean(name = "user.management.profileDao")
public UserProfileRepository getProfileRepository() {
return new UserProfileDaoImpl(getIdGenerator());
}
#Autowired
#Bean(name = "user.management.impl.service.idgenerator.IdGenerator")
public IdGenerator getIdGenerator() {
return new IdGenerator();
}
#Autowired
#Bean(name = "user.management.AuthenticatorRepositoryDAO")
public AuthenticatorRepository getAuthenticationRepository() {
return new AuthenticatorRepositoryDAO();
}
#Autowired
#Bean(name = "user.management.dobLdapSourceRepository")
public LdapSourceRepository getLdapSourceRepository() {
return new LdapSourceRepositoryImpl();
}
#Autowired
#Bean(name = "user.management.dobOpenIdAuthenticationProviderRepository")
public OpenIdAuthenticationProviderRepository getOpenIdAuthenticationProviderRepository() {
return new OpenIdAuthenticationProviderRepositoryDAO();
}
}
Service java config
#Configuration
public class UserManagementConfiguration {
#Autowired
#Bean(name = "user.management.services.UserService")
public UserService getUserService(...) {
return new UserServiceImpl(...);
}
}
DAO
#Transactional(propagation = Propagation.REQUIRED)
public class UserDao implements UserRepository {
public UserDao(...) {}
#PersistenceContext(unitName = "umPersistenceUnit")
private EntityManager entityManager;
#Override
#Transactional(propagation = Propagation.REQUIRED)
public void persistUser(#Nonnull MutableUser user) {
entityManager.persist(user);
}
}
Service
public class UserServiceImpl implements UserService {
public UserServiceImpl(...);
}
#Override
#Transactional
public void persistUser(#Nonnull User user) {
userDAO.persistUser(mutableUser);
}
}
Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0">
<persistence-unit name="umPersistenceUnit">
<!-- Forces Hibernate to work with old-style annotations -->
<properties>
<property name="hibernate.id.new_generator_mappings" value="false"></property>
</properties>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>user.management.impl.repository.pojo.User</class>
</persistence-unit>
</persistence>
Spring config.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:annotation-config/>
<tx:annotation-driven transaction-manager="user.management.transactionManager"/>
<bean class="user.management.config.UserManagementConfiguration"/>
<bean class="user.management.config.HibernateConfiguration"/>
</bean>
</beans>
Test config is identical, only uses different dataSource and jdbc.driver to connect to an in-memory h2.
What i've already tried:
Using <tx:annotation-driven> and pointing to a correct transactionManagerFactory
Removing transaction-type="RESOURCE_LOCAL" from persistence.xml
Adding #Transactional to specific methods in Dao and Service classes.
Checked that entityManagerFactory is only loaded once on server startup
Debugged the server to see if some other entityManager is called. I can clearly see my persistence unit and qualifier in its properties.
Probably some other minor fixes that popped up when i tried to google about this issue.
When i deploy code like posted here, i can see no errors in log, etc. When entitymanager.flush() is added it becomes javax.persistence.TransactionRequiredException: no transaction is in progress
Providing transaction-type="JTA" to persistence.xml explicitly fixed the issue, entities are being persisted to the DB sucessfully.
As per Spring documentation,
the steps to use Spring JdbcTemplate is as follows:
<?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">
<!-- Scans within the base package of the application for #Components to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
And then,
#Repository
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
Basically, the JdbcTemplate is created inside the Component class using the setter for datasource.
Is there anything wrong with doing it this way instead so that there is exactly ONE instance of jdbcTemplate in the application?
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource"
/>
And then injecting the jdbcTemplate itself directly into the Component
#Repository
public class JdbcCorporateEventDao implements CorporateEventDao {
#Resource("jdbcTemplate")
private JdbcTemplate jdbcTemplate;
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
Is there a reason why the jdbcTemplate itself must not be injected into the component class directly?
SGB
You can do what you want. The javadoc of JdbcTemplate even clearly says it:
Can be used within a service implementation via direct instantiation with a DataSource reference, or get prepared in an application context and given to services as bean reference.
In the spring-context.xml add the following and
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
and directly you can use jdbcTemplate by autowiring like
#Autowired JdbcTemplate jdbcTemplate;
example:
this.jdbcTemplate.query("select * from ******",new RowMapper());
You can also do it like
#Configuration
#Import({PersistenceConfig.class})
#ComponentScan(basePackageClasses = {
ServiceMarker.class,
RepositoryMarker.class }
)
public class AppConfig {
/**
* To resolve ${} in #Values, you must register a static PropertySourcesPlaceholderConfigurer in either XML or
* annotation configuration file.
*/
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
PersistenceConfig
#Configuration
#PropertySource(value = { "classpath:database/jdbc.properties" })
#EnableTransactionManagement
public class PersistenceConfig {
#Autowired
private Environment env;
/**
* The #Bean annotation is used to declare a Spring bean and the DI requirements. The #Bean annotation is equivalent to
* the <bean> tag, the method name is equivalent to the id attribute within the <bean> tag.
*
* <bean id="mySqlDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.mysql.driverClassName}"
p:url="${jdbc.mysql.url}"
p:username="${jdbc.mysql.username}"
p:password="${jdbc.mysql.password}" />
*
* #return
*/
#Bean(destroyMethod = "close")
public DataSource mySqlDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.mysql.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.mysql.url"));
dataSource.setUsername(env.getProperty("jdbc.mysql.username"));
dataSource.setPassword(env.getProperty("jdbc.mysql.password"));
return dataSource;
}
#Bean(destroyMethod = "close")
public DataSource ls360DataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.ls360.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.ls360.url"));
dataSource.setUsername(env.getProperty("jdbc.ls360.username"));
dataSource.setPassword(env.getProperty("jdbc.ls360.password"));
return dataSource;
}
}
MySqlDaoImpl
#Repository
public class MySqlDaoImpl implements MySqlDao{
private static final Logger logger = LogManager.getLogger();
#Inject
private DataSource mySqlDataSource;
private JdbcTemplate mySqlJdbcTemplate;
#PostConstruct
public void afterPropertiesSet() throws Exception {
if (mySqlDataSource == null) {
throw new BeanCreationException("Must set mySqlDataSource on " + this.getClass().getName());
}
this.mySqlJdbcTemplate = new JdbcTemplate(mySqlDataSource);
}
#Override
public void callStoredProcedure(String storedProcedureName, Map<String, Object> inParamMap) throws Exception {
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(mySqlJdbcTemplate).withProcedureName(storedProcedureName);
SqlParameterSource in = new MapSqlParameterSource(inParamMap);
logger.info("Calling stored Procedure: " + storedProcedureName);
Map<String, Object> simpleJdbcCallResult = simpleJdbcCall.execute(in);
logger.info("Stored Procedure Result: " + simpleJdbcCallResult);
}
}
Main
public static void main(String[] args ) {
try (GenericApplicationContext springContext = new AnnotationConfigApplicationContext(AppConfig.class)) {
MySQLDao mySqlDao = springContext.getBean(MySQLDaoImpl.class);
try {
Map<String, Object> inParamMap = new HashMap<String, Object>();
inParamMap.put("iCourseId", 1);
mySqlCourseRenewalDao.callStoredProcedure("usp_processCourseRenewal", inParamMap);
} catch (Exception e) {
logger.error("Exception occurs", e);
}
} catch (Exception e) {
logger.error("Exception occurs in loading Spring context: ", e);
}
}
Thanks
There's nothing technically wrong with injecting and sharing JdbcTemplate in place of the underlying DataSource.
However, there are some design drawbacks to sharing JdbcTemplate, which may favor injecting DataSource instead:
Use of JdbcTemplate is an implementation detail of the DAO, and we like to keep those details hidden
JdbcTemplate is lightweight, so sharing it for efficiency is likely a premature optimization
Sharing a JdbcTemplate isn't risk-free as it has some mutable state (in addition to mutable state in the underlying DataSource)
That's assuming the the typical case where JdbcTemplate is used with its default configuration (fetchSize, maxRows, etc). If configuration is required, that may drive the design in a particular direction, e.g. a shared pre-configured JdbcTemplate injected from context, or even multiple JdbcTemplate instances owned by a single DAO.
In spring JavaConfig, I can define property source and inject into Environment
#PropertySource("classpath:application.properties")
#Inject private Environment environment;
How do I do that if in xml?
I am using context:property-placeholder, and on the JavaConfig class #ImportResource to import the xml. But I cannot retrieve property defined in the properties file using environment.getProperty("xx")
<context:property-placeholder location="classpath:application.properties" />
AFAIK, there is no way of doing this by pure XML. Anyways, here is a little code I did this morning:
First, the test:
public class EnvironmentTests {
#Test
public void addPropertiesToEnvironmentTest() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"testContext.xml");
Environment environment = context.getEnvironment();
String world = environment.getProperty("hello");
assertNotNull(world);
assertEquals("world", world);
System.out.println("Hello " + world);
}
}
Then the class:
public class PropertySourcesAdderBean implements InitializingBean,
ApplicationContextAware {
private Properties properties;
private ApplicationContext applicationContext;
public PropertySourcesAdderBean() {
}
public void afterPropertiesSet() throws Exception {
PropertiesPropertySource propertySource = new PropertiesPropertySource(
"helloWorldProps", this.properties);
ConfigurableEnvironment environment = (ConfigurableEnvironment) this.applicationContext
.getEnvironment();
environment.getPropertySources().addFirst(propertySource);
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
And the testContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<util:properties id="props" location="classpath:props.properties" />
<bean id="propertySources" class="org.mael.stackoverflow.testing.PropertySourcesAdderBean">
<property name="properties" ref="props" />
</bean>
</beans>
And the props.properties file:
hello=world
It is pretty simple, just use a ApplicationContextAware bean and get the ConfigurableEnvironment from the (Web)ApplicationContext. Then just add a PropertiesPropertySource to the MutablePropertySources
If what you need is just access the property "xx" of the file "application.properties", you can accomplish that without Java code by declaring the following bean in your xml file:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="application.properties"/>
</bean>
Then if you want to inject the property in a bean just reference it as a variable:
<bean id="myBean" class="foo.bar.MyClass">
<property name="myProperty" value="${xx}"/>
</bean>