I am trying to access the #Bean class in my another class but I keep on getting BeanCreationException.
Here is my config file with all #Bean classes
CassandraConfig.class
package com.spring.cassandra.daos;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.cassandra.config.CassandraClusterFactoryBean;
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
#Configuration
#PropertySource(value = { "classpath:com/spring/cassandra/props/cassandra.properties" })
public class CassandraConfig {
public CassandraConfig(){
System.out.println("In Cassandra Config");
}
#Autowired
private Environment env;
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints(env.getProperty("cassandra.contactpoints"));
cluster.setPort(Integer.parseInt(env.getProperty("cassandra.port")));
return cluster;
}
#Bean
public CassandraMappingContext mappingContext() {
return new BasicCassandraMappingContext();
}
#Bean
public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(env.getProperty("cassandra.keyspace"));
session.setConverter(converter());
session.setSchemaAction(SchemaAction.NONE);
return session;
}
#Bean
public CassandraOperations cassandraTemplate() throws Exception {
return new CassandraTemplate(session().getObject());
}
}
CassandraTransaction.class
package com.spring.cassandra.daos;
import java.util.List;
import org.springframework.cassandra.core.RowMapper;
import org.springframework.config.java.context.JavaConfigApplicationContext;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.stereotype.Component;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.exceptions.DriverException;
#Component
public class CassandraTransaction {
/*Error in this below two lines while trying to access the CassandraTemplate.class*/
JavaConfigApplicationContext context = new JavaConfigApplicationContext(CassandraConfig.class);
CassandraOperations cassandraOperations = (CassandraOperations) context.getBean(CassandraTemplate.class);
private CassandraTransaction(){
System.out.println("In Cassandra Transaction");
getUsers();
}
public void getUsers(){
System.out.println("Retreiving users");
List<User> results = cassandraOperations.query("select * from users", new RowMapper<User>() {
#Override
public User mapRow(Row row, int rowNum) throws DriverException {
User user = new User();
user.setUsername(row.getString("username"));
user.setName(row.getString("name"));
user.setEmail(row.getString("email"));
return user;
}
});
for(User user : results){
System.out.println(user);
}
}
}
Exception in console:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cassandraTransaction' defined in file [D:\Spring\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\CassandraApplication\WEB-INF\classes\com\spring\cassandra\daos\CassandraTransaction.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.spring.cassandra.daos.CassandraTransaction]: Constructor threw exception; nested exception is java.lang.Error: Unresolved compilation problems:
I am in need of access the CassandraOperations cassandraTemplate() bean in order to use the template to query the cassandra DB but I am facing this error in accessing the bean class.
Should we use ApplicationContext only in main() methods? I see many examples using ApplicationContext in public static void main(){}, if so how could that be possible using main method in spring? I am confused on what has to be done. Please explain.
Related
Bean creation is supposed to be a Singleton in the spring container, correct? I am migrating a Spring configuration file to Spring Boot with Annotations. I have the below code, but it seems to call the "mySingletonBean()" every time that it is used within another bean creation. It is my understanding that the #Bean annotation is supposed to be Singleton by default. Am I creating my Beans correctly?
#Bean
public SomeBean mySingletonBean() {
SomeBean mybean = new SomeBean();
mybean.setName = "Name";
return mybean;
}
#Bean
public Bean1 bean1() {
Bean1 bean1 = new Bean1();
bean1.setBean(mySingletonBean());
return bean1;
}
#Bean
public Bean2 bean2() {
Bean2 bean2 = new Bean2();
bean2.setBean(mySingletonBean());
return bean2;
}
Spring is proxying your application context class, and takes care of all the context related stuff like instantiating, caching etc. of the beans.
Run this small test, feel free to debug to see what class the configuration class became:
package stackoverflow;
import java.util.Arrays;
import java.util.Date;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertTrue;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Test.MyContext.class)
public class Test {
#Autowired
private ApplicationContext applicationContext;
#Autowired
#Qualifier("myStringBean")
private String myStringBean;
#Autowired
private SomeBean someBean;
#Configuration
static class MyContext {
#Bean
public String myStringBean() {
return String.valueOf(new Date().getTime());
}
#Bean
public SomeBean mySomeBean() {
return new SomeBean(myStringBean());
}
}
#org.junit.Test
public void test() {
assertTrue(myStringBean == applicationContext.getBean("myStringBean"));
assertTrue(myStringBean == someBean.getValue());
System.out.println(Arrays.asList(applicationContext.getBean("myStringBean"), myStringBean, someBean.getValue()));
}
static class SomeBean {
private String value;
public SomeBean(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}
Spring Boot is smart framework and method mySingletonBean() will starts only once time, don't worry about it. U can start debug mode and see .
I'm building JPA configuration with multiple persistence units using different in-memory datasources, but the configuration fails resolving the qualified datasource for entity manager factory bean with the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method emfb in datasources.Application$PersistenceConfiguration required a single bean, but 2 were found:
- ds1: defined by method 'ds1' in class path resource [datasources/Application$PersistenceConfiguration.class]
- ds2: defined by method 'ds2' in class path resource [datasources/Application$PersistenceConfiguration.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Here is the sample application
package datasources;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.sql.DataSource;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.apache.log4j.Logger;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.stereotype.Component;
#Configuration
#EnableAutoConfiguration(exclude = {
// HibernateJpaAutoConfiguration.class,
// DataSourceAutoConfiguration.class
JtaAutoConfiguration.class
})
#ComponentScan
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.build()
.run(args);
}
#Component
#Path("/ds")
public static class DsApi {
private final static Logger logger = Logger.getLogger(DsApi.class);
#Autowired(required = false)
#Qualifier("ds1")
private DataSource ds;
#GET
public String ds() {
logger.info("ds");
return ds.toString();
}
}
#Component
#Path("/em")
public static class EmApi {
private final static Logger logger = Logger.getLogger(EmApi.class);
#PersistenceContext(unitName = "ds2", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
#GET
public String em() {
logger.info("em");
return em.toString();
}
}
#Configuration
#ApplicationPath("/jersey")
public static class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(DsApi.class);
register(EmApi.class);
}
}
#Configuration
public static class PersistenceConfiguration {
#Bean
#Qualifier("ds1")
public DataSource ds1() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
#Qualifier("ds2")
public DataSource ds2() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
#Primary
#Autowired
public LocalContainerEntityManagerFactoryBean emfb(#Qualifier("ds1") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(Application.class)
.persistenceUnit("ds1")
.build();
}
#Bean
#Autowired
public LocalContainerEntityManagerFactoryBean emfb2(#Qualifier("ds2") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(Application.class)
.persistenceUnit("ds2")
.build();
}
}
}
The error is indicating that at some point in the application, a bean is being injected by the type DataSource and not being qualified by name at that point.
It does not matter that you have added #Qualifier in one location. The injection is failing in some other location that has not been qualified. It's not your fault though because that location is in Spring Boot's DataSourceAutoConfiguration which you should be able to see in your stack trace, below the piece that you have posted.
I would recommend excluding DataSourceAutoConfiguration i.e. #SpringBootApplication(exclude = DataSourceAutoConfiguration.class). Otherwise, this configuration is only being applied to the bean you have made #Primary. Unless you know exactly what that is, it is likely to result in subtle and unexpected differences in behaviour between your DataSources.
Declare one of your DataSource as #Primary.
Also you have 2 beans of same type - LocalContainerEntityManagerFactoryBean, declare one of them #Primary as well, as follows:
#Configuration
public static class PersistenceConfiguration {
#Bean
#Primary
public DataSource ds1() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
public DataSource ds2() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
#Primary
#Autowired
public LocalContainerEntityManagerFactoryBean emfb(#Qualifier("ds1") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(DemoApplication.class)
.persistenceUnit("ds1")
.build();
}
#Bean
#Autowired
public LocalContainerEntityManagerFactoryBean emfb2(#Qualifier("ds2") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(DemoApplication.class)
.persistenceUnit("ds2")
.build();
}
}
Try declaring the datasource beans outside the static class . I.e directly in Application.java
I'm trying to configure spring batch inside spring boot project and I want to use it without data source. I've found that ResourcelessTransactionManager is the way to go but I cannot make it work. Problem is I already have 3 another dataSources defined, but I don't want to use any of them in springBatch.
I've checked default implementation DefaultBatchConfigurer and if it is not able to find dataSource it will do exactly what I want. Problem is I've 3 of them and dont want to use any.
Please dont suggest to use hsql or other in memory DB as I dont want that.
I got around this problem by extending the DefaultBatchConfigurer class so that it ignores any DataSource, as a consequence it will configure a map-based JobRepository.
Example:
#Configuration
#EnableBatchProcessing
public class BatchConfig extends DefaultBatchConfigurer {
#Override
public void setDataSource(DataSource dataSource) {
//This BatchConfigurer ignores any DataSource
}
}
In my case I persist data to Cassandra. If you are using spring-boot-starter-batch it is expected to provide a DataSource which is not yet implemented but you can trick the configuration like in the following steps:
Step1:
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class SampleSpringBatchApplication{
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "true");
SpringApplication.run(SampleSpringBatchApplication.class, args);
}
}
Step2:
#Configuration
#EnableBatchProcessing
public class SampleBatchJob extends DefaultBatchConfigurer {
//..
#Override
public void setDataSource(DataSource dataSource) {
}
//..
}
You can try excluding the DataSourceAutoConfiguration in #SpringBootApplication. See the sample code below.
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
#EnableBatchProcessing
public class SampleBatchApplication {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Bean
protected Tasklet tasklet() {
return new Tasklet() {
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext context) {
return RepeatStatus.FINISHED;
}
};
}
#Bean
public Job job() throws Exception {
return this.jobs.get("job").start(step1()).build();
}
#Bean
protected Step step1() throws Exception {
return this.steps.get("step1").tasklet(tasklet()).build();
}
public static void main(String[] args) throws Exception {
System.exit(SpringApplication.exit(SpringApplication.run(SampleBatchApplication.class, args)));
}
}
And sample test class
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.test.rule.OutputCapture;
import static org.assertj.core.api.Assertions.assertThat;
public class SampleBatchApplicationTests {
#Rule
public OutputCapture outputCapture = new OutputCapture();
#Test
public void testDefaultSettings() throws Exception {
assertThat(SpringApplication.exit(SpringApplication.run(SampleBatchApplication.class))).isEqualTo(0);
String output = this.outputCapture.toString();
assertThat(output).contains("completed with the following parameters");
}
}
If you have more than one DataSource in your configuration (regardless of if you want to use them or not) you need to define your own BatchConfigurer. It's the only way the framework knows what to do in situations like that.
You can read more about the BatchConfigurer in the documentation here: http://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/configuration/annotation/BatchConfigurer.html
We had the similar problem, we were using spring boot JDBC and we did not want to store spring batch tables in the DB, but we still wanted to use spring's transaction management for our DataSource.
We ended up implementing own BatchConfigurer.
#Component
public class TablelessBatchConfigurer implements BatchConfigurer {
private final PlatformTransactionManager transactionManager;
private final JobRepository jobRepository;
private final JobLauncher jobLauncher;
private final JobExplorer jobExplorer;
private final DataSource dataSource;
#Autowired
public TablelessBatchConfigurer(DataSource dataSource) {
this.dataSource = dataSource;
this.transactionManager = new DataSourceTransactionManager(this.dataSource);
try {
final MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean(this.transactionManager);
jobRepositoryFactory.afterPropertiesSet();
this.jobRepository = jobRepositoryFactory.getObject();
final MapJobExplorerFactoryBean jobExplorerFactory = new MapJobExplorerFactoryBean(jobRepositoryFactory);
jobExplorerFactory.afterPropertiesSet();
this.jobExplorer = jobExplorerFactory.getObject();
final SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
simpleJobLauncher.setJobRepository(this.jobRepository);
simpleJobLauncher.afterPropertiesSet();
this.jobLauncher = simpleJobLauncher;
} catch (Exception e) {
throw new BatchConfigurationException(e);
}
}
// ... override getters
}
and setting up initializer to false
spring.batch.initializer.enabled=false
I want to retrieve the current user in my controller methods with the #AuthenticationPrincipal annotation. The docs state the following:
Annotation that binds a method parameter or method return value to the Authentication.getPrincipal().
But in fact I get the Authentication object instead of Authentication.getPrincipal().
This is my simple controller method:
#RequestMapping("/")
public #ResponseBody String index(#AuthenticationPrincipal final WindowsAuthenticationToken user) {
return String.format("Welcome to the home page, %s!", user.getName());
}
WindowsAuthenticationToken implements Authentication. In this implementation getPrincipal returns a WindowsPrincipal.
The controller method above works, but when I change the arguments type to WindowsPrincipal and try to access the website, I get the following error page:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Mar 03 15:13:52 CET 2015
There was an unexpected error (type=Internal Server Error, status=500).
argument type mismatch HandlerMethod details: Controller [pkg.HomeController] Method [public java.lang.String pkg.HomeController.index(waffle.servlet.WindowsPrincipal)] Resolved arguments: [0] [type=waffle.spring.WindowsAuthenticationToken] [value=waffle.spring.WindowsAuthenticationToken#121a2581]
This is my configuration file:
package pkg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import waffle.servlet.spi.BasicSecurityFilterProvider;
import waffle.servlet.spi.NegotiateSecurityFilterProvider;
import waffle.servlet.spi.SecurityFilterProvider;
import waffle.servlet.spi.SecurityFilterProviderCollection;
import waffle.spring.NegotiateSecurityFilter;
import waffle.spring.NegotiateSecurityFilterEntryPoint;
import waffle.windows.auth.impl.WindowsAuthProviderImpl;
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint;
#Autowired
private NegotiateSecurityFilter waffleNegotiateSecurityFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(negotiateSecurityFilterEntryPoint).and()
.addFilterBefore(waffleNegotiateSecurityFilter, BasicAuthenticationFilter.class).authorizeRequests()
.anyRequest().fullyAuthenticated();
}
#Bean
public WindowsAuthProviderImpl waffleAuthProvider() {
return new WindowsAuthProviderImpl();
}
#Bean
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(
final WindowsAuthProviderImpl waffleAuthProvider) {
return new NegotiateSecurityFilterProvider(waffleAuthProvider);
}
#Bean
public BasicSecurityFilterProvider basicSecurityFilterProvider(final WindowsAuthProviderImpl waffleAuthProvider) {
return new BasicSecurityFilterProvider(waffleAuthProvider);
}
#Bean
public SecurityFilterProviderCollection waffleSecurityFilterProviderCollection(
final NegotiateSecurityFilterProvider negotiateSecurityFilterProvider,
final BasicSecurityFilterProvider basicSecurityFilterProvider) {
final SecurityFilterProvider[] providers = { negotiateSecurityFilterProvider, basicSecurityFilterProvider };
return new SecurityFilterProviderCollection(providers);
}
#Bean
public NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint(
final SecurityFilterProviderCollection waffleSecurityFilterProviderCollection) {
final NegotiateSecurityFilterEntryPoint entryPoint = new NegotiateSecurityFilterEntryPoint();
entryPoint.setProvider(waffleSecurityFilterProviderCollection);
return entryPoint;
}
#Bean
public NegotiateSecurityFilter waffleNegotiateSecurityFilter(
final SecurityFilterProviderCollection waffleSecurityFilterProviderCollection) {
final NegotiateSecurityFilter filter = new NegotiateSecurityFilter();
filter.setProvider(waffleSecurityFilterProviderCollection);
return filter;
}
}
Why is the behaviour different from how it should be?
My principal object did not implement UserDetails. Because WindowsPrincipal is a class of an external library I could not make any changes to it. In the end I created a new filter that wraps the WindowsPrincipal in a class that implements UserDetails. Now I get the correct principal object using #AuthenticationPrincipal.
It is because your WindowsPrincipal implements Principal. Remove the implements clause and it will work again. I had the same problem and this resolved it.
I'm writing a DAL with Spring Data and Hibernate but I'm running into a IllegalArgumentException exception which is stopping my work.
Here is the DALConf.java class which contains DataSource and persistence exception translation processor configurations
package my.dal.config;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
#Configuration
#ComponentScan(basePackages = { "my.dal" })
#PropertySource("classpath:dbconnection.properties")
public class DALConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_POOL_INITIAL_SIZE = "pool.initialsize";
private static final String PROPERTY_NAME_POOL_MAX_IDLE = "pool.maxidle";
#Resource
private Environment environment;
#Bean
public DataSource dataSource() throws Exception
{
Properties props = new Properties();
props.put("driverClassName", environment.getProperty(PROPERTY_NAME_DATABASE_DRIVER));
props.put("url", environment.getProperty(PROPERTY_NAME_DATABASE_URL));
props.put("username", environment.getProperty(PROPERTY_NAME_DATABASE_USERNAME));
props.put("password", environment.getProperty(PROPERTY_NAME_DATABASE_PASSWORD));
props.put("initialSize", environment.getProperty(PROPERTY_NAME_POOL_INITIAL_SIZE));
props.put("maxIdle", environment.getProperty(PROPERTY_NAME_POOL_MAX_IDLE));
BasicDataSource bds = BasicDataSourceFactory.createDataSource(props);
return bds;
}
#Bean
public PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor()
{
PersistenceExceptionTranslationPostProcessor b = new PersistenceExceptionTranslationPostProcessor();
return b;
}
}
Then here is the HibernateConfig.class which contains Hibernate configurations
package my.dal.config;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#ComponentScan(basePackages = { "my.dal" })
#PropertySource("classpath:hibernate.properties")
#EnableTransactionManagement
public class HibernateConfig {
private static final String PROPERTY_NAME_DAL_CLASSES_PACKAGE = "hibernate.dal.package";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.showsql";
#Resource
private Environment environment;
#Autowired
DataSource dataSource;
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
lsfb.setPackagesToScan(environment.getProperty(PROPERTY_NAME_DAL_CLASSES_PACKAGE));
Properties hibernateProperties = new Properties();
hibernateProperties.put("dialect", environment.getProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
hibernateProperties.put("show_sql", environment.getProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
lsfb.setHibernateProperties(hibernateProperties);
lsfb.setDataSource(dataSource);
return lsfb.getObject();
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator(){
return new HibernateExceptionTranslator();
}
#Bean
public HibernateTransactionManager transactionManager()
{
// HERE THE EXCEPTION IS THROWN
HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory());
return htm;
}
}
This is the DAO UserDAO.java
package my.dal.dao;
import my.models.dal.User;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
#Repository
public class UserDAO
{
private SessionFactory sessionFactory;
#Autowired
public UserDAO(SessionFactory sessionFactory) {
this.sessionFactory=sessionFactory;
}
#Transactional
public int insert(User user) {
return (Integer) sessionFactory.getCurrentSession().save(user);
}
#Transactional
public User getByUsername(String username) {
return (User) sessionFactory.getCurrentSession().get(User.class, username);
}
#Transactional
public void update(User user) {
sessionFactory.getCurrentSession().merge(user);
}
#Transactional
public void delete(String username) {
User u = getByUsername(username);
sessionFactory.getCurrentSession().delete(u);
}
}
Finally, this is the test class DALTest.java
package my.dal.tests;
import static org.junit.Assert.assertTrue;
import my.dal.config.DALConfig;
import my.dal.config.HibernateConfig;
import my.dal.dao.UserDAO;
import my.models.dal.User;
import org.hibernate.SessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#ContextConfiguration(classes = { DALConfig.class, HibernateConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class DALTest {
#Autowired
SessionFactory sessionFactory;
#Test
public void testGetUser() {
UserDAO userDAO = new UserDAO(sessionFactory);
User user = null;
user = userDAO.getByUsername("mrossi");
assertTrue(null != user);
}
}
The execution of the test ends with the following exception
...
Caused by: java.lang.IllegalArgumentException: Property 'sessionFactory' is required
at org.springframework.orm.hibernate4.HibernateTransactionManager.afterPropertiesSet(HibernateTransactionManager.java:247)
at org.springframework.orm.hibernate4.HibernateTransactionManager.<init>(HibernateTransactionManager.java:130)
at my.dal.config.HibernateConfig.transactionManager(HibernateConfig.java:66)
...
It is thrown at this line
HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory());
It seems like Spring cannot instantiate the sessionFactory bean but I don't know what could be the problem...
What do you think about that?
Thank you
You forgot to call
lsfb.afterPropertiesSet()
before getting the object from lsfb. afterPropertiesSet() is the method that builds and exposes the session factory.
One way to fix that is that you are using Constructor Injection for the sessionFactory which is not working with annotation exposed bean well. (Not sure how Spring 4 makes any improvement on that. I only used Spring 3.5 and below)
I would recommend to use getter/setter method injection for that in UserDAO.java
private SessionFactory sessionFactory;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
So annotation can get the bean.
As JB Nizet suggested, you're missing the call to afterPropertiesSet()which is needed if you handle the object lifecycle yourself. I'd like to propose a slightly better version of your config to avoid this issue, which you might run into in other cases as well.
Whenever you configure a FactoryBean in JavaConfig, return the factory and refer to the actual object to be produced on the client methods. In your example this would look something like this:
#Configuration
class YourConfig {
#Bean
public LocalSessionFactoryBean sessionFactory() {
// setup factory
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory factory) {
return new HibernateTransactionManager(factory);
}
}
As you can see, we don't manually invoke any lifecycle callbacks on the factory as we have to return it as is. Spring will do that for us. It will also invoke ….getObject() on it to create the actual SessionFactory and hand it into the #Bean method to create the transaction manager.