Mr. Polywhirl
Mr. Polywhirl

Reputation: 48630

Spring: BeanCreationException

I am trying to get the session from the session factory to use with my CRUD methods. I tried to set the session in the constructor, but the bean has not been initialized by then. How do I initialize it after the bean is created?

Stacktrace

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDaoImpl' defined in file [/home/ryan/workspace/com-byteslounge-spring-tx/target/classes/com/byteslounge/spring/tx/dao/impl/UserDaoImpl.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.byteslounge.spring.tx.dao.impl.UserDaoImpl]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1011)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:957)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:490)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:607)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at com.byteslounge.spring.tx.Main.main(Main.java:19)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.byteslounge.spring.tx.dao.impl.UserDaoImpl]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1004)
    ... 13 more
Caused by: java.lang.NullPointerException
    at com.byteslounge.spring.tx.dao.impl.UserDaoImpl.<init>(UserDaoImpl.java:24)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
    ... 15 more

UserDaoImpl.java

package com.byteslounge.spring.tx.dao.impl;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.byteslounge.spring.tx.dao.UserDao;
import com.byteslounge.spring.tx.domain.User;

@Service
public class UserDaoImpl implements UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    private Session session;

    public UserDaoImpl() {
        session = sessionFactory.getCurrentSession();
    }

    public SessionFactory getSessionFactory() {
        return this.sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void insertUser(User user) {
        session.save(user);
    }

    public void removeUserByName(String username) {
        User user = null;
        try {
            getUser(username);
        } catch (IndexOutOfBoundsException e) {
            System.out.println(username + " does not exist!");
        } finally {
            if (user != null)
                session.delete(user);
        }
    }

    public User getUserById(int userId) {
        return (User) session.get(User.class, userId);
    }

    public User getUser(String username) {
        // Query query = sessionFactory.getCurrentSession().createQuery("from User where username = :username");
        // query.setParameter("username", username);

        Query query = session.getNamedQuery(User.FIND_USER_BY_USERNAME).setString("username", username);
        return (User) query.list().get(0);
    }

    @SuppressWarnings("unchecked")
    public List<User> getUsers() {
        Criteria criteria = session.createCriteria(User.class);
        return criteria.list();
    }
}

applicationContext.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"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
    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">

    <tx:annotation-driven />

    <context:component-scan base-package="com.byteslounge.spring.tx.dao.impl" />
    <context:component-scan base-package="com.byteslounge.spring.tx.user.impl" />

    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>database.properties</value>
        </property>
    </bean>

    <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>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
        <property name="packagesToScan" value="com.byteslounge.spring.tx.domain" />
    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager"
        p:sessionFactory-ref="sessionFactory">
    </bean>

</beans>

Upvotes: 2

Views: 14169

Answers (2)

Steve Oh
Steve Oh

Reputation: 1159

As Ralf points out, the constructor is executed too early and Spring did not inject anything at this point.

Further, you may not hold a reference to Session as it relies on the SessionContext mechanism, which in turn may be thread bound or servlet request bound. Your bean may has a wider scope (e.g. singleton) and you will permanently store the session from the first caller or something!

I suggest to always get the session from sessionFactory when needed inside the bean.

Upvotes: 1

Caesar Ralf
Caesar Ralf

Reputation: 2233

Use the @PostConstruct tag and get the session then. Your beans will only be injected after your UserDaoImpl is created and @PostConstruct method will only be called by Spring after all your beans are injected.

@Service
public class UserDaoImpl implements UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    private Session session;

    public UserDaoImpl() {
    }

    @PostConstruct
    public void init() {
        this.session = sessionFactory.getCurrentSession();
    }
    ...
}

Another way (better in my opnion) is inject by constructor and getting the current session only when you need it.

    ...
    private final SessionFactory sessionFactory;

    @Autowired
    public UserDaoImpl(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    private Session currentSession() {
        return this.sessionFactory.getCurrentSession();
    }

EDIT: Use the second way preferably, because if you get the session only after construct, calls to your DAO won't work once that session is expired. Always use SessionFactory#getCurrentSession() when starting make calls at your transactional methods.

Upvotes: 3

Related Questions