0

I have been receiving this error all the time when using Lazy evaluation in a ManyToMany relationship:

LazyInitializationException: failed to lazily initialize a collection of roles, could not initialize proxy - no Session

I know that using Eager fetch type will work fine, but this will be a headache and a performance lag to the application, so, I have to continue considering the Lazy fetch type.

I read that when using Spring Data JPA, @Transactional will initialize the lazy relations, but it doesn't seem to take effect with me.

Here is how my app look like:

Entities

Course Entity

@Entity
@Table(name = "course")
@Transactional
public class Course {


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

@Column(name = "course_name")
private String courseName;

@Column(name = "course_description")
private String courseDescription;

@ManyToMany(cascade = {CascadeType.MERGE,CascadeType.DETACH,CascadeType.REFRESH,CascadeType.PERSIST})
@JoinTable(
        name = "course_student",
        joinColumns = @JoinColumn(name = "course_id"),
        inverseJoinColumns = @JoinColumn(name = "student_id") )
private List<Student> students;

Student Entity

@Entity
@Table(name = "students")
public class Student {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

private String name;

private String grade;

@ManyToMany(cascade = {CascadeType.MERGE,CascadeType.DETACH,CascadeType.REFRESH,CascadeType.PERSIST},fetch = FetchType.EAGER)
@JoinTable(
        name = "course_student",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id") )
List<Course> courses;

Controller

@Controller
@RequestMapping("/courses")
@Transactional
public class CourseController {

....

@GetMapping("get-joined-students")
ModelAndView getJoined() {

    Course course = courseRepo.findById(6).get();

    for (Student student :
            course.getStudents()) {

        System.out.println("Student: " + student);
    }


    ModelAndView modelAndView = new ModelAndView("redirect:list");

    return modelAndView;
}

}

Repositories

Course Repository

@Repository
public interface CourseRepo extends JpaRepository<Course,Integer> {}

Student Repository

@Repository
public interface StudentRepo extends JpaRepository<Student,Integer> { }

Servlet Configuration File

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"  ....    >

<context:annotation-config/>

<context:component-scan base-package="com.company.springdemo"/>

<mvc:annotation-driven/>

....

<jpa:repositories transaction-manager-ref="transactionManagerJPA" base-package="com.company.springdemo" entity-manager-factory-ref="EMF" />

<bean id="transactionManagerJPA" class="org.springframework.orm.jpa.JpaTransactionManager" >
    <property name="dataSource" ref="myDataSource" />
</bean>

<tx:annotation-driven transaction-manager="transactionManagerJPA" />


<bean id="EMF"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="packagesToScan" value="com.company.springdemo.entity" />
    <property name="dataSource" ref="myDataSource" />

    <property name="jpaProperties">
        <props>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</prop>
            <prop key="hibernate.show_sql">false</prop>
            <prop key="hibernate.format_sql">true</prop>
        </props>
    </property>

    <property name="persistenceProvider">
        <bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
    </property>
</bean>

What potentially could I be doing wrong?

securecurve
  • 5,589
  • 5
  • 45
  • 80

2 Answers2

0

Using @Transactional for none public methods needs aspectj dependency. In the code the related method seems to be package private. Changing to public should solve the problem, i hope. I had the same problem and this post helps me Does Spring @Transactional attribute work on a private method?

Devilluminati
  • 328
  • 1
  • 15
-1

I think you are mixing up things here. LazyInitializationException is popping up as you are trying to fetch the relation when the transaction is already over.

You have multiple choices:

  1. Access the relation within the transaction
  2. Use EAGER fetching
  3. Use custom queries with FETCH JOIN
  4. Use Projections

Also read my article for more details: https://arnoldgalovics.com/lazyinitializationexception-demystified/ https://arnoldgalovics.com/using-projections-in-your-data-access-layer/

Arnold Galovics
  • 3,246
  • 3
  • 22
  • 33
  • Thanks for answer. Yes, I understand that this exception is popping up becuase we access the relation when the trasaction is dead, so, my question still stands: I want to initialize the lazy entities without fetching them eagerly, using fetch join, nor using projections – securecurve May 27 '18 at 15:50
  • In other words, I wish if you can elaborate more on your first suggestion in terms of using Spring Data JPA interface classified in the question. Thanks again! – securecurve May 27 '18 at 15:52
  • Also, if possible, how can @Transactional annotation can solve this problem, in case I configured it wrong – securecurve May 27 '18 at 15:53
  • Your controller is marked with `@Transactional` anyway, so it's supposed to be working. Which Spring version are you using? Did you import the `@Transactional` annotation from Spring or from JavaEE? – Arnold Galovics May 27 '18 at 18:19
  • I use Spring 5.0.5 … Yes, @Transactional annotation is already imported as shown here: `import javax.transaction.Transactional; ` – securecurve May 27 '18 at 18:45
  • You imported the wrong one. It should come from `org.springframework.transaction.annotation` – Arnold Galovics May 27 '18 at 18:56
  • will try it now – securecurve May 27 '18 at 18:57
  • okay, in this case I guess this is not a Spring Boot app, thus you probably have configured two different application contexts for the app. One for the WebApp and one as a child context, thus the transactionality is not working properly unless you define the transaction management in both. I'd say let's extract the logic from the controller to a different class, mark it with `@Transactional` and define it as a bean and it will work definitely. – Arnold Galovics May 27 '18 at 19:05
  • Correct, I'm not using Spring Boot, I'm using Spring MVC. Do you want me to add the same code outside the controller code under a class annotated as a @Component? Then what should I do to make it work under the controller? – securecurve May 27 '18 at 19:13
  • I like the way you approached the problem … Please bear with me till we fix it. – securecurve May 27 '18 at 19:14
  • 1. yes, 2. insert the tx:annotation-driven config for both configurations 3. it's better if you structure your application in a more maintainable and cleaner way. Transactionality has nothing to do with the controllers at all. Extract it to a service class which defines your business logic, it's not the controller's responsibility to do the work. – Arnold Galovics May 27 '18 at 19:16
  • I already tried to add that code inside a @Service, and injected that service inside the controller ... the only variable I'm trying now is the path of the Transactinoal library … which I'm trying now – securecurve May 27 '18 at 19:20
  • Also, please have a look on the config file added last in the OP, and please suggest modifications, becuase tx:annotation-driven part is already there – securecurve May 27 '18 at 19:21
  • I just tested adding the code to a Service and injecting that service into the controller, and the problem still persists – securecurve May 27 '18 at 19:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/171878/discussion-between-securecurve-and-galovics). – securecurve May 27 '18 at 19:26
  • Can we please move to the discussion room for smoother communication? – securecurve May 27 '18 at 19:27
  • Hello @galovics any hope solving this?? – securecurve May 29 '18 at 10:12