【问题标题】:How to load the collection of a LAZY association for an already found entity如何为已找到的实体加载 LAZY 关联的集合
【发布时间】:2020-07-10 22:56:34
【问题描述】:

考虑以下几点:

@Entity
@Table(name = "cars")
public class Car {

    @Id
    private int id;
    private String name;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "ownerCar")
    private Set<Wheel> wheels = new HashSet<>();

    private Car() {
    }

    public Car(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public Set<Wheel> getWheels() {
        return wheels;
    }
}
@Entity
@Table(name = "wheels")
public class Wheel {

    @Id
    private int id;

    @ManyToOne
    private Car ownerCar;

    private Wheel() {
    }

    public Wheel(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public Car getOwnerCar() {
        return ownerCar;
    }

    public void setOwnerCar(Car ownerCar) {
        this.ownerCar = ownerCar;
    }
}

@Override //CommandLineRunner
public void run(String... args) throws Exception {
    Car car = new Car(1);
    car.setName("Ferrari");

    Wheel wheel = new Wheel(1);
    wheel.setOwnerCar(car);

    car.getWheels().add(wheel);
    carService.saveCar(car);

    // Assume we have found the car already
    car = carService.getById(1).get();

    // Load the wheels of this car
    carService.loadWheelsForCar(car);

    System.out.println(car.getWheels().size());
}

上面的代码会抛出org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: test.app.Car.wheels

我的问题是如何实现loadWheelsForCar(Car c)方法,而无需再次找车。

换句话说,如何SELECT * FROM WHEELS WHERE owner_car_id = car.id 并将结果添加到集合中。我可能可以手动完成,但这是唯一的方法吗?

我知道当没有活动会话时会抛出LazyInitializationException@Transactional 不会导致创建新会话吗?)。我尝试过:

@Transactional
public void loadWheelsForCar(Car c) {
    c.getWheels().size(); // will load wheels
}

但抛出异常。

如果是XY problem,我不这样做的原因(CarService):

@Transactional
public Optional<Car> getByIdWithWheels(int carId) {
    Optional<Car> possibleCar = carRepository.findById(carId);
    possibleCar.ifPresent(c -> c.getWheels().size());
    return possibleCar;
}

是因为主应用程序中的父实体(Car 实体)有多个 @OneToMany 关联,其中一些还具有嵌套关联。如果我遵循这种方法,我最终会得到多个@Transactional 方法,例如getCarByIdWithWheelsgetCarByIdWithSeatsgetCarByIdWithSeatsAndWheels 等。但我想要的是能够执行以下操作:

Car c = carService.getById(1);
carService.loadWheelsForCar(c);
carService.loadSeatsForCar(c);

我尝试了一些在网络上找到的东西,但我找到的每个解决方案都是“重新加载”“Car”实体。

【问题讨论】:

    标签: java hibernate jpa spring-data-jpa spring-data


    【解决方案1】:

    我知道当没有活动会话时会抛出 LazyInitializationException(@Transactional 不会导致创建一个新会话吗?)

    Hibernate 确实会创建一个新会话。但该会话是新鲜的,它不知道您的Car c,因为您在另一个会话中获取了该车,因此该车没有在该新会话中获取、保存或更新。

    • 您知道您正在经过的汽车与数据库中的汽车完全相同,并且两者之间没有更新。不幸的是,hibernate 中没有 API 可以告诉您这是一辆与数据库中完全相同的汽车,不要用它的数据库检查它,只要相信你。没有像session.attach(car)这样的api。

    • 所以你能做的最接近的是session.merge(car)。 Hibernate 会发出一个 select 来检查你正在经过的汽车和数据库中的汽车是否相同,并且由于相同,它不会发出更新。作为该选择的一部分,轮子也会被加载。

        @Autowired
        EntityManager em;
    
        @Transactional
        public Car loadWheelsForCar(Car car){
            Car merged = em.merge(car);
            merged.getWheels().size();
            return merged;
        }
    
    • 你会看到上面的方法没有发布任何更新,只是一个类似于下面的选择查询
        select
            car0_.id as id1_0_1_,
            car0_.name as name2_0_1_,
            wheels1_.owner_car_id as owner_ca3_1_3_,
            wheels1_.id as id1_1_3_,
            wheels1_.id as id1_1_0_,
            wheels1_.name as name2_1_0_,
            wheels1_.owner_car_id as owner_ca3_1_0_ 
        from
            cars car0_ 
        left outer join
            wheels wheels1_ 
                on car0_.id=wheels1_.owner_car_id 
        where
            car0_.id=?
    

    【讨论】:

    • 其实这里还有更多内容。 stackoverflow.com/questions/912659/…
    • 是的。那是对的。它确实在一个查询中同时选择汽车和车轮,以检查它是否与数据库中的相同
    猜你喜欢
    • 2015-04-14
    • 1970-01-01
    • 2013-07-11
    • 1970-01-01
    • 1970-01-01
    • 2017-01-19
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多