Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LazyInitializationException Spring Boot

I know there are a lot of similar threads out there but i just can't figure it out from those threads on how to overcome this problem.

I have 3 classes Car, Brand, Color. A Car has just one Brand and a list of Colors. Brand has a List of Cars. Color does not have any relation.

Getters, Setters, ToString and Constructors are not provided for simplicity sake. I'm able to save objects into database and database is already populated.

--------------------------------------------------------------------------------

@Entity
@Table(catalog = "spring_project")
public class Car {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String model;

@ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable( name = "car_color", catalog = "spring_project",
            joinColumns         = { @JoinColumn(name = "car_id") },
            inverseJoinColumns  = { @JoinColumn(name = "colors_id") }
)
private List<Color> colors = new ArrayList<>();

@ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name="brand_id", referencedColumnName="id")
private Brand brand;

--------------------------------------------------------------------------------

@Entity
@Table(catalog = "spring_project")
public class Brand {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;

@OneToMany(mappedBy = "brand", fetch = FetchType.LAZY)
private List<Car> cars = new ArrayList<>();

--------------------------------------------------------------------------------

@Entity
@Table(catalog = "spring_project")
public class Color {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

--------------------------------------------------------------------------------

Everything runs just fine if i fetch like Eager, but i know it is a bad practice and it should be used Lazy loading instead. But i keep getting the LazyInitializationException.

I understand from the error that a session is required but i dont know how to provide one since im working with Spring Data JPA neither where i should declare one...

@SpringBootApplication
public class SrpingJpaApplication {

private static final Logger log = 
LoggerFactory.getLogger(SrpingJpaApplication.class);

public static void main(String[] args) {
    SpringApplication.run(SrpingJpaApplication.class, args);
}

@Bean
public CommandLineRunner demo(CarRepository carRepository,
                              ColorRepository colorRepository,
                              BrandRepository brandRepository) {
    return (args) -> {

        log.info("Reads all cars....");
        for (Car c : carRepository.findAll()) {
            System.out.println(c.toString());
        }

    };
}

}

Thank you so much.

Edited----->>>

The error is thrown on c.toString();

Error: Caused by: org.hibernate.LazyInitializationException: could not initialize proxy [com.readiness.moita.SrpingJPA.Models.Brand#1] - no Session

like image 397
Bruno Miguel Avatar asked Dec 18 '18 15:12

Bruno Miguel


1 Answers

The default for the @OneToMany annotation is FetchType.LAZY so your collections are loaded lazily.

In order to be able to access the collection after you've retrieved the object you need to be in a transactional context (you need an open session)

When you call:

carRepository.findAll();

internally a new session is created, the object is retrieved and as soon as the findAll method returns the session is closed.

What you should do is make sure you have an open session whenever you access the lazy collection in your Car object (which the toString does).

The simplest way is to have another service handle the car loading and annotate the showCars method with @Transactional the method is in another service because of the way AOP proxies are handled.

@Service
public CarService {

    final CarRepository carRepository;

    public CarService(CarRepository carRepository) {
        this.carRepository = carRepository;
    }    

    @Transactional
    public void showCars(String... args) {
        for (Car c : carRepository.findAll()) {
            System.out.println(c.toString());
        }
    }   
}

and then you call:

@Bean
public CommandLineRunner demo(CarService carService) {
    return (args) -> service.showCars(args);
}
like image 194
Radu Sebastian LAZIN Avatar answered Sep 20 '22 11:09

Radu Sebastian LAZIN