When I inject my sessionFactory Bean using Java based configuration for Hibernate my bean is null and I don't know why. I've scoured the internet for answers but couldn't find any. I've looked over my configuration and compared it against guides online. Any answers are greatly appreciated.
Here's the exact error
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "org.hibernate.SessionFactory.getCurrentSession()" because "com.example.demo.DemoApplication.sessionFactory" is null
Here's my code
HibernateConfiguration File, Annotation based.
#Configuration
#EnableTransactionManagement
public class HibernateConfig {
#Bean
#Scope //By default the scope is singleton which means that the IOC will only create a single instance of the bean and return that one reference for subsequent calls for that bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan( packagesToScan()); //Model packages to scan
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
//Direct Physical Connection Information
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
//List of Entities to scan
#Bean
public String [] packagesToScan() {
return new String [] { "com.example.demo.Entities.Student" };
}
//Configures properties of our hibernate configuration, dialect,
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "create");
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
hibernateProperties.setProperty("show_sql", "true");
hibernateProperties.setProperty("current_session_context_class", "thread");
return hibernateProperties;
}
}
My Main application where I'm attempting to inject my Session Factory singleton bean for use.
#SpringBootApplication
public class DemoApplication {
#Autowired
static SessionFactory sessionFactory;
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AnimalConfig.class, HibernateConfig.class); // Makes the sessionFactory bean known to the IOC
Session currentSession = sessionFactory.getCurrentSession();
(( ConfigurableApplicationContext )ctx).close(); //Close the applicationContext
SpringApplication.run(DemoApplication.class, args);
}
}
The container gets started when SpringApplication.run invokes. I think before all of this an injection and the usage of that bean does not makes sense. First you need to fire Springapplication and then the rest of the business logic.
Your bean isn't scanned and initialize at the moment of the injection.
Possible solution :
#Component
public class IOCAfterInitializationListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
static SessionFactory sessionFactory;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Session currentSession = sessionFactory.getCurrentSession();
//do whatever you want
}
}
Related
I'm currently learning Spring and Hibernate. I have a StudentDAOImpl class that contains all my database queries. I want to get an instance of this bean in my main function and run the query methods there all in my main function for testing. I'm trying to do this by calling the getBean() method because the name of the bean is the class but first letter is lowercase if I'm not mistaken. The compiler is giving me this error
Exception in thread "main org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'studentDAOImpl' available**
Why can't Spring find my StudentDAOImpl bean? Thank you for your time in reading this post.
Here's my code
Main Function
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AnimalConfig.class,HibernateConfig.class); // Makes the sessionFactory bean known to the IOC
StudentDAOImpl student = (StudentDAOImpl)ctx.getBean("studentDAOImpl");
Student aStudent = new Student("dasdasdas","dasdadas","dsadasdasda#gmail.com");
student.addStudent(aStudent);
(( ConfigurableApplicationContext )ctx).close(); //Close the applicationContext
SpringApplication.run(DemoApplication.class, args);
}
StudentDAOImpl Class
#Repository //Sets up componenent scanning for DI for our services
public class StudentDAOImpl implements StudentDAO{
#Autowired
SessionFactory sessionFactory;
//Queries Ommited from post for space saving purposes
}
HibernateConfiguration File
#Configuration
#EnableTransactionManagement
public class HibernateConfig {
#Bean(name = "sessionFactory")
#Scope("singleton")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(packagesToScan());
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
dataSource.setUsername("root");
dataSource.setPassword("danielL45");
return dataSource;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager
= new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private String[] packagesToScan(){
return new String[] {
"com.example.demo.Entities.Student"
};
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "update");
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
hibernateProperties.setProperty("current_session_context_class", "thread");
return hibernateProperties;
}
}
Use this:
StudentDAOImpl student = ctx.getBean(StudentDAOImpl.class)
This is a more secure way to get the bean that we need, since we may have only one bean with this class, using a string to select a Spring Bean has a propability of not working when not writing the name correctly.
I am working on a Spring-MVC application in which I have 2 data-sources defined for different types of tasks. Currently, during migration from XML to Java, I stumbled upon a requirement to add a new HibernateTransactionManager object for #Transactional to work. For the 2 different data-sources, I have 2 separate SessionFactory instances. But when I try to create yet another instance of HibernateTransactionManager with secondary data-source, I get a non-unique exception.
Do I require a secondary HibernateTransactionManager instance for the config I am posting or will 1 suffice? If required, how can I create one? Thank you.
Error log :
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: primary_tx,extended_tx
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:368)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:367)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:271)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springfram
WebConfig.java :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.ourapp.spring"})
#EnableTransactionManagement
#EnableCaching
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean(name="primary_tx")
public HibernateTransactionManager getPrimaryTransactionManager() throws IOException {
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(sessionFactory().getObject());
return txName;
}
#Bean(name="extended_tx")
public HibernateTransactionManager txName() throws IOException {
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(getExtendedSessionFactory().getObject());
return txName;
}
#Bean
#Qualifier("sessionFactory_origin")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(new DataSourceConfig().primaryDataSource());
sessionFactory.setPackagesToScan("com.ourapp.spring");
return sessionFactory;
}
#Bean
#Qualifier("sessionFactory_extended")
public LocalSessionFactoryBean getExtendedSessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(new DataSourceConfig_Extended().secondaryDataSource());
sessionFactory.setPackagesToScan("com.ourapp.spring");
return sessionFactory;
}
}
Typical Service Layer method :
#Service
#Transactional("primary_tx")
public class ChatRoomMembersServiceImpl implements ChatRoomMembersService{
private final ChatRoomMembersDAO chatRoomMembersDAO;
#Autowired
public ChatRoomMembersServiceImpl(ChatRoomMembersDAO chatRoomMembersDAO){
this.chatRoomMembersDAO = chatRoomMembersDAO;
}
}
Typical DAO layer method :
#Repository
#Transactional("primary_tx")
public class ChatRoomMembersDAOImpl implements ChatRoomMembersDAO{
#Autowired
#Qualifier(value = "sessionFactory_origin")
private SessionFactory sessionFactory;
#Autowired
#Qualifier(value = "sessionFactory_extended")
private SessionFactory sessionFactory_extended;
}
Now, whenever required, I am referring to the extended SessionFactory instance. Right now, I have annotated DAO layer methods requiring extended sessionFactory with secondary_tx, but it's not working. Thank you. :-)
The answer to your question
Do I require to define separate TransactionManager for 2 data sources?
is YES
Java8 onwards you can have same annotation(provided that the annotation is marked #Repeatable) multiple times on a method. Other way to accomplish this is to gave a custom annotation which takes care of one of the datasources.
The exception you are getting is because there are two qualifying beans for transation management. You need a Qualifier for this.
Instead of:
#Bean(name="primary_tx")
public HibernateTransactionManager getPrimaryTransactionManager() throws IOException {
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(sessionFactory().getObject());
return txName;
}
Use
#Bean #Qualifier("primary_tx")
public HibernateTransactionManager getPrimaryTransactionManager() throws IOException {
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(sessionFactory().getObject());
return txName;
}
Now, Let say You want to make the transactions in my method m1 as atomic. The following is what you need
#Transactional("primary_tx")
public void m1(){
}
I'm sessionFactory on my service.
#Component
public class MyService {
#Autowired
private SessionFactory sessionFactory;
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
public void persistData(){
...
tx.commit();
session.close();
}
I call this service from
#Component
public class CommandLineApp implements CommandLineRunner {
#Autowired
private MyService MyService
public void run(String... args) throws Exception {
MyService.persistData();
}
}
My main class that start spring boot:
#SpringBootApplication
public class App{
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
When I call MyService it's getting the error nullPointException. When I debug the code I can see that the sessionFactory is null. Why is sessionFactory is not being injected on MyService?
The problem here is the SessionFactory bean is still not
available to the spring container for you to inject.
So first you need to bootstrap it.
Create an #configuration file
or add #Configuration to your Main.java and bootstrap your beans like below
#Configuration
public class HibernateSessionProvider {
#Bean // If using hibernate.cfg.xml
public void getSessionFactory() {
AnnotationSessionFactoryBean annotationSessionFactoryBean = new AnnotationSessionFactoryBean();
...
return annotationSessionFactoryBean()
}
#Bean // If using java based config to provide DataSource beans , hibernate config
public void getSessionFactory() {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
...
return localSessionFactoryBean()
}
}
Good evening,
what is the correct and common approach of handling two or more databases?
Consider this HibernateConfiguration class configuring only one datasource:
#Configuration #EnableTransactionManagement
#PropertySource(value = { "classpath:hibernate.properties" })
public class HibernateConfiguration {
#Autowired
private Environment env;
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// ... setting data source
return dataSource;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
// ... setting Hibernate properties
return properties;
}
#Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setPackagesToScan(new String[] { "POJOs'" });
sessionFactory.setHibernateProperties(getHibernateProperties());
return sessionFactory;
}
#Bean public HibernateTransactionManager transactionManager(SessionFactory sf) {
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sf);
return htm;
}
}
Is recommended to let one class configure one datasource? Or is enough to configure all at once? How do I specify in Dao class which SessionFactory will be used and what is the recommended approach in case of switching two exact same databases on two different hosting servers?
The example DAOs. First I need to switch between Foo and Bar.
#Repository
public class RepositoryImpl implements RepositoryDao {
#Autowired // Here I need to switch between databases "foo" and "bar"
private SessionFactory sessionFactory;
...
The second one I need fixed on example database Foo.
#Repository
public class FooImpl implements FooDao {
#Autowired // Here I need fixed on "Foo"
private SessionFactory sessionFactory;
One approach
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
return fooDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("app.datasource.bar")
public BasicDataSource barDataSource() {
return (BasicDataSource) DataSourceBuilder.create()
.type(BasicDataSource.class).build();
}
Spring multiple datasources config
Other approach could be : loading different mapping (orm.xml) from persistence.xml or refer to different schemas in Entity classes.
In an spring boot project, is there a way to use injected object inside a #Bean method. In my example following, isdatasourceUse() method able to acccess injected Datasource (either from dev or war profile)
#EnableScheduling
#Configuration
#EnableAspectJAutoProxy
#Profile({ "dev", "war" })
public class AppConfig {
Logger logger = LoggerFactory.getLogger(AppConfig.class);
#Autowired
DBPropertyBean dbPropertyBean;
#Bean(destroyMethod = "")
#Profile("war")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName(dbPropertyBean.getJndiName());
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
bean.afterPropertiesSet();
return (DataSource) bean.getObject();
}
#Bean(destroyMethod = "close")
#Profile("dev")
public DataSource getDataSource() throws Exception {
com.mchange.v2.c3p0.ComboPooledDataSource ds = new com.mchange.v2.c3p0.ComboPooledDataSource();
ds.setUser(dbPropertyBean.getDsUsername());
ds.setPassword(dbPropertyBean.getDsPassword());
ds.setJdbcUrl(dbPropertyBean.getDsJdbcUrl());
ds.setDriverClass(dbPropertyBean.getDsDriverClass());
ds.setMaxPoolSize(dbPropertyBean.getDsMaxPoolSize());
ds.setMinPoolSize(dbPropertyBean.getDsMinPoolSize());
ds.setInitialPoolSize(dbPropertyBean.getDsInitPoolSize());
ds.setAcquireIncrement(dbPropertyBean.getDsAcquireInc());
ds.setAcquireRetryAttempts(dbPropertyBean.getDsAcquireRetryAtt());
ds.setPreferredTestQuery(dbPropertyBean.getPreferredTestQuery());
ds.setIdleConnectionTestPeriod(dbPropertyBean.getIdleConnectionTestPeriod());
return ds;
}
#Bean
public void datasourceUse() {
//How to user datasource here
}
}
Use it like below:
#Autowired
public void datasourceUse(DataSource dataSource) {
System.out.println(dataSource);
}