LazyInitializationException Spring Boot

Я знаю, что есть много похожих тем, но я просто не могу понять из этих тем, как решить эту проблему.

У меня 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] - нет сеанса

Возможный дубликат: stackoverflow.com/questions/49894587/…

The Head Rush 18.12.2018 17:18

Попробуйте аннотировать свой метод demo с помощью @Transactional. Таким образом, Spring занимается управлением сеансом.

Aditya Narayan Dixit 18.12.2018 18:52
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
6
3
8 150
2

Ответы 2

Поскольку 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

Я упомянул об этом в своем вопросе ... мне бы хотелось, чтобы у меня была возможность установить ленивую выборку и вручную открывать сеанс по запросу через код позади

Bruno Miguel 18.12.2018 18:32

Мой ответ ниже также отвечает на этот вопрос.

Radu Sebastian LAZIN 07.11.2021 04:53

По умолчанию для аннотации @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);
}

Другие вопросы по теме