【问题标题】:LazyInitializationException Spring BootLazyInitializationException Spring Boot
【发布时间】:2019-05-19 02:12:45
【问题描述】:

我知道那里有很多类似的线程,但我只是无法从这些线程中弄清楚如何克服这个问题。

我有 3 类汽车、品牌、颜色。 一辆车只有一个品牌和一个颜色列表。 品牌有一个汽车清单。 颜色没有任何关系。

为简单起见,没有提供 Getter、Setter、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] - 无会话

【问题讨论】:

标签: java spring spring-data-jpa


【解决方案1】:

@OneToMany 注释的默认值是 FetchType.LAZY,因此您的集合会延迟加载。

为了能够在您检索到对象后访问该集合,您需要处于事务上下文中(您需要一个打开的会话)

当你打电话时:

carRepository.findAll();

在内部创建一个新会话,检索对象并在findAll 方法返回后立即关闭会话。

您应该做的是确保在您访问 Car 对象中的惰性集合时有一个打开的会话(toString 会这样做)。

最简单的方法是让另一个服务处理汽车加载,并用@Transactional 注释showCars 方法,由于处理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);
}

【讨论】:

    【解决方案2】:

    由于BrandFetchType 是惰性的,它不会自动加载到调用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;
    

    如果您不想将 fetch 类型设置为 eager,那么您需要将对 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

    【讨论】:

    • 我下面的回答也回答了这个问题。
    猜你喜欢
    • 2019-12-09
    • 1970-01-01
    • 2021-04-23
    • 2014-08-08
    • 2018-04-03
    • 2015-07-31
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多