1

I'm using Spring 3.2.9 with multilayered architecture design broken down to 3 modules web, service, repository. In repository i defined a generic DAO class which i inherit by other entity specific DAO classes.

The problem is that when i try to lazy fetch collection of my entity from service layer, I always get LazyInitializationException. I have tried putting @Transactional on my service class, but It appears to not work. I can only avoid exception if I initialize all lazy collections right away in the DAO class method (and only if i annotate DAO class with @Transactional), but I want to fetch those collections only when they are needed in bussiness logic, and not all in advance.

Strange thing is that @Transactional works only in DAO layer, but not in Service layer, where it should be used. I found a couple of ways to work around this issue, but I am interested to really underestend and solve this issue, and not only make make code work.

repository MODULE:

@Repository
public abstract class GenericDao<T> {

    protected Class<T> entityClass;

    @PersistenceContext
    protected EntityManager entityManager;

    .........

    public T findById(long id) {
        T entity = entityManager.find(entityClass, id);
        if (entity == null) {
            throw new EntityNotFoundException(entityClass.getSimpleName(), id);
        }

        return entity;
    }
}

My service class in service MODULE:

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @SuppressWarnings("SpringJavaAutowiringInspection")
    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Transactional(readOnly = true)
    @Override
    public UserDto getUserById(long id) {
        User user = userDao.findById(id);

        return new UserDto(user);
    }

The DTO constructor tries to access user.getTeams() and then exception occurs. Instead of that, collection should be fetched with additional query to DB.

CONFIGURATION:

repository configuration: ......some other configurations like datasource...

<!--EntityManagerFactory-->
<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="jpaPropertyMap">
        <map>
            <entry key="hibernate.dialect" value="${hibernate_dialect}"/>
            <entry key="hibernate.hbm2ddl.auto" value="${hibernate_Hbm2ddlAuto}"/>
            <entry key="hibernate.show_sql" value="${hibernate_showSql}"/>
            <entry key="hibernate.format_sql" value="${hibernate_formatSql}"/>
        </map>
    </property>
</bean>

persistence.xml:

<persistence-unit name="persistenceUnit">
    ...other classes..
    <class>com.example.entity.User</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
    </properties>
</persistence-unit>

service configuration:

<import resource="classpath*:META-INF/repositoryApplicationContext.xml"/>

<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
LW001
  • 2,452
  • 6
  • 27
  • 36
zozo
  • 11
  • 4

2 Answers2

0

It looks like @OneToMany association between the user and his or her teams which is lazy loaded. When you assign them to Dto it is still proxy not real collection. Put a break point there and see it in debug mode. Keep in mind any request (get a team or its size make it alive). One way is to fetch them in query, but

entityManager.find(entityClass, id)

doesn't have option for it. You may use

Hibernate.initialize(user.getTeams())
Andriy Slobodyanyk
  • 1,965
  • 14
  • 15
  • Yes i know for that, that is the same like invoking getTeams().size(). Thanks anyways – zozo Sep 15 '17 at 13:39
0

Found the answer after few days of headache. I had to move:

<import resource="classpath*:META-INF/repositoryApplicationContext.xml"/>
<tx:annotation-driven/>

from repositoryConfiguration.xml to dispatcher-servlet.xml since that is the parent Spring context.

Thanks for the help.

zozo
  • 11
  • 4