Search code examples
javaspringhibernatejpaspring-transactions

Spring Data JPA with Transactions


I am developing an application using Spring Data JPA with Transactions. Although the version of Spring I'm on (4.0.0) is able to use JavaConfig, I prefer to stick with XML.

I have this config:

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jpa="http://www.springframework.org/schema/data/jpa"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">

<jpa:repositories base-package="repo"/>
<context:component-scan base-package="service" />
<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost/staffing?transformedBitIsBoolean=true"/>
    <property name="username" value="root"/>
    <property name="password" value="vivupdip1`"/>
</bean>

<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="showSql" value="true"/>
    <property name="generateDdl" value="true"/>
    <property name="database" value="MYSQL"/>
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
    <property name="packagesToScan" value="model"/>
    <property name="jpaPropertyMap">
        <map>
            <entry key="hibernate.hbm2ddl.auto" value="validate"/>
            <entry key="hibernate.format_sql" value="true" />
        </map>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

</beans>

My single (so far) repository is this:

package repo;

import org.springframework.data.jpa.repository.JpaRepository;
import model.Volunteer;

public interface VolunteerRepo extends JpaRepository<Volunteer, Integer> {
}

I also have a Service interface in the service package:

public interface VolunteerService {
  public List<Volunteer> findAll();
}

and the implementation:

@Service
@Transactional
public class VolunteerServiceImpl implements VolunteerService {

    @Autowired VolunteerRepo repo;

    public List<Volunteer> findAll() {
      return repo.findAll();
    }

}

called from a Controller in the controller package:

@RestController
public class VolunteerController {

  @Autowired VolunteerService vs;

  @RequestMapping(value = "/volunteers")
  List<Volunteer> getVolunteers() {
    return vs.findAll();
  }
}

The Volunteer domain object is quite complex and is related to various other domain objects.

When I issue the correct request in the browser, I get the following exception:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: model.Volunteer.volunteerSessions, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->model.Volunteer["sessions"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: model.Volunteer.volunteerSessions, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->model.Volunteer["sessions"])

which, as far as I understand, is moaning about not having a session, which (I think) can be fixed by configuring transactions properly. But, my attempt as configuring transactions seems not to be correct, but I cannot work out why.


Solution

  • Your errormessage stating that there is no session available is preceded by:

    Could not write JSON
    

    This tells you that the proxy was accessed when attempting to serialize the object into JSON. This is happening in your RestController, which indeed is not transactional, so has no session bound, and that is why the lazy-loading failed.

    Possible solutions are:

    • Add a DAO layer and transform your DB objects into simple java POJOs when returning from your VolunteerServiceImpl
    • You can try to annotate your controller as @Transactional
    • You might want to consider switching off lazyloading if you know that you will always need that "volunteer" data because you are returning in a JSON response