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
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With