使用spring规范从另一个对象获取连接到属性

tzcvj98z  于 2021-07-13  发布在  Java
关注(0)|答案(2)|浏览(257)

我有以下实体:

@Entity
@Table(name = "user_data")
public class UserData {
    ...
    @ManyToOne
    private User user;
    ...
}

@Entity
@Table(name = "user_cars")
public class UserCar {
    ...
    private Integer userId;
    ...
}

@Entity
@Table(name = "users")
public class User {
    ...
    @OneToMany(mappedBy = "userId", cascade = CascadeType.ALL)
    private List<UserCar> userCars;
    ...
}

如您所见,usercars的加载很慢(我不打算更改它)。现在我使用规范来获取用户数据:

public Page<UserData> getUserData(final SpecificationParameters parameters) {
    return userDataRepository.findAll(createSpecification(parameters), parameters.pageable);
}

private Specification<UserData> createSpecification(final SpecificationParameters parameters) {
    final var clazz = UserData.class;
    return Specification.where(buildUser(parameters.getUserId()));
}

private Specification<UserData> buildUser(final Integer userId) {
    return (root, criteriaQuery, criteriaBuilder) -> {
        if (Objects.nonNull(userId)) {
            final Join<UserData, User> joinParent = root.join("user");
            return criteriaBuilder.equal(joinParent.get("id"), userId);
        } else {
            return criteriaBuilder.isTrue(criteriaBuilder.literal(true));
        }
    };
}

但是我不知道如何添加fetchjoin子句来获取用户汽车。我尝试在不同的地方添加它,我得到了lazyinitializationexception(所以它不起作用)或者其他一些异常。。。

rta7y2nd

rta7y2nd1#

与前面的回答略有不同,但我认为jcc提到的想法是正确的,即“hibernate正在抱怨,因为它无法找到usercars关系的所有者,在本例中是用户。”
为此,我想知道对象关系引擎是否因为直接链接到userid(原语)而不是用户(实体)而变得混乱。我不确定它是否可以假定“userid”原语必然意味着与用户实体的连接。
是否可以尝试重新安排Map,使其不在联接表中使用整数userid,而是使用对象本身,然后查看是否允许实体管理器更好地理解您的查询?
因此Map可能如下所示:

@Entity
@Table(name = "user_cars")
public class UserCar {
    ...

    @ManyToOne
    @JoinColumn(name="user_id", nullable=false) // Assuming it's called user_id in this table
    private User user;
    ...
}

@Entity
@Table(name = "users")
public class User {
    ...
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<UserCar> userCars;
    ...
}

这也将更符合https://www.baeldung.com/hibernate-one-to-many

q3aa0525

q3aa05252#

除了问题评论中提供的建议@crizzis外,请尝试 join fetch 这个 user 关系;在您报告的错误中:

org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list

hibernate抱怨找不到主人, user 在这种情况下 userCars 关系。
这在某种程度上是奇怪的,因为 @ManyToOne 这段关系将急切地得到回报 user 实体和它也将在获得 userData 但是hibernate可能在实际的获取阶段之前执行查询分析。如果有人能就这一点提供一些额外的见解,那就太好了。
话虽如此,请考虑将fetch策略显式设置为 FetchType.LAZY 在你的 @ManyToOne 关系:

@Entity
@Table(name = "user_data")
public class UserData {
    ...
    @ManyToOne(fetch= FetchType.LAZY)
    private User user;
    ...
}

你的 Specification 代码可以如下所示:

public Page<UserData> getUserData(final SpecificationParameters parameters) {
    return userDataRepository.findAll(createSpecification(parameters), parameters.pageable);
}

private Specification<UserData> createSpecification(final SpecificationParameters parameters) {
    final var clazz = UserData.class;
    return Specification.where(buildUser(parameters.getUserId()));
}

private Specification<UserData> buildUser(final Integer userId) {
    return (root, criteriaQuery, criteriaBuilder) -> {
        // Fetch user and associated userCars
        final Join<UserData, User> joinParent = (Join<UserData, User>)root.fetch("user");
        joinParent.fetch("userCars");

        // Apply filter, when provided
        if (Objects.nonNull(userId)) {
            return criteriaBuilder.equal(joinParent.get("id"), userId);
        } else {
            return criteriaBuilder.isTrue(criteriaBuilder.literal(true));
        }
    };
}

我之前没有注意实体关系本身,但是atmas给了你一个很好的建议,事实上,它将是处理这种关系中的数据的更有效的方法。
至少,定义 User 以及 UserCars 使用 @JoinColumn 注解,而不是通过非实体字段进行Map,以防止实体出现错误或不正确的行为。例如,考虑:

@Entity
@Table(name = "users")
public class User {
    ...
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "user_id")
    private List<UserCar> userCars;
    ...
}

相关问题