Я знаю, что есть много похожих тем, но я просто не могу понять из этих тем, как решить эту проблему.
У меня 3 класса Авто, Марка, Цвет. У автомобиля есть только одна марка и список цветов. У бренда есть список автомобилей. Цвет не имеет никакого отношения.
Геттеры, сеттеры, ToString и конструкторы не предусмотрены для простоты. Я могу сохранять объекты в базе данных, и база данных уже заполнена.
@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;
Все работает нормально, если я получаю как Eager, но я знаю, что это плохая практика и вместо этого следует использовать ленивую загрузку. Но я продолжаю получать исключение LazyInitializationException.
Я понимаю из-за ошибки, что требуется сеанс, но я не знаю, как его предоставить, так как я работаю с Spring Data JPA, и я не должен его объявлять ...
@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());
}
};
}
}
Большое спасибо.
Отредактировано ----- >>>
Ошибка возникает в c.toString ();
Ошибка: вызвана: org.hibernate.LazyInitializationException: не удалось инициализировать прокси [com.readiness.moita.SrpingJPA.Models.Brand # 1] - нет сеанса
Возможный дубликат LazyInitializationException: не удалось инициализировать прокси - нет сеанса
Попробуйте аннотировать свой метод demo с помощью @Transactional. Таким образом, Spring занимается управлением сеансом.




Поскольку FetchTypeBrand является ленивым, он не будет автоматически загружен в сеанс с вызовом fetchAll(). Чтобы он автоматически загружался в сеанс, вам необходимо:
Изменять
@ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "brand_id", referencedColumnName = "id")
private Brand brand;
к
@ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
Бывший
@ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "brand_id", referencedColumnName = "id")
private Brand brand;
Если вы не хотите, чтобы тип выборки был активным, вам нужно переместить вызов toString в метод службы Ex.
@Component
public CarService implements ICarService {
@Autowired
CarRepository carRepository;
@Transactional
public void printAllCars() {
for (Car c : carRepository.findAll()) {
System.out.println(c.toString());
}
}
}
Однако правильный способ сделать это - написать запрос с критериями или hql
Я упомянул об этом в своем вопросе ... мне бы хотелось, чтобы у меня была возможность установить ленивую выборку и вручную открывать сеанс по запросу через код позади
Мой ответ ниже также отвечает на этот вопрос.
По умолчанию для аннотации @OneToMany используется FetchType.LAZY, поэтому ваши коллекции загружаются лениво.
Чтобы иметь возможность получить доступ к коллекции после получения объекта, вам необходимо находиться в транзакционном контексте (вам нужен открытый сеанс)
Когда вы звоните:
carRepository.findAll();
внутри создается новый сеанс, объект извлекается, и как только метод findAll возвращает, сеанс закрывается.
Что вам следует сделать, так это убедиться, что у вас есть открытый сеанс всякий раз, когда вы обращаетесь к ленивой коллекции в вашем объекте Car (что делает toString).
Самый простой способ - поручить другой службе обрабатывать загрузку автомобиля и аннотировать метод showCars с помощью @Transactional. Метод находится в другой службе из-за способа обработки прокси AOP.
@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());
}
}
}
а потом звоните:
@Bean
public CommandLineRunner demo(CarService carService) {
return (args) -> service.showCars(args);
}
Возможный дубликат: stackoverflow.com/questions/49894587/…