1

已经有类似的帖子了。由于这已经较旧,我希望从那时起有所改变(FetchMode 如何在 Spring Data JPA 中工作

如果关系用 EAGER 标记,我想一次性运行所有 jpa 存储库#findById。但是,spring 数据忽略了来自 hibernate 的 EAGER 规范和 FETCH.JOIN 注释。

是否有一个通用的解决方案,所有 findById 查询都在一个选择中执行?

我不想为每个查询编写单独的 JPL 或 EntityGraph。有人知道通用解决方案吗?

4

1 回答 1

1

JpaRepository

最简单的选择是编写一个JpaRepository<T, Id>. 这仍然是一个自定义存储库。但是,您不必编写这么多代码。您主要必须为每个相关类编写一个存储库接口,并findById(Long id)用图形注释该方法。优点是,如果您编辑实体,存储库方法将不需要任何更改,因为您在实体类本身中定义了实体图。

@Entity
@NamedEntityGraph(name = "Department.detail",
                  attributeNodes = @NamedAttributeNode("employees"))
public class Department {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(fetch = FetchType.LAZY)
    private List<Employee> employees;

    // ...
}

public interface DepartmentRepository extends JpaRepository<Department, Long> {

    @EntityGraph(value = "Department.detail", type = EntityGraphType.LOAD)
    List<Department> findById(Long id);
}

由于 Spring 数据会忽略@Fetch(Fetchmode.JOIN)注释或信息fetch = FetchType.EAGER,因此您无法影响连接在实体本身中的方式。

JPQL 查询您需要的地方

另一种选择可以被认为是一种糟糕的软件工程风格:您可以在需要的地方直接调用数据库查询。这意味着您执行通常会在存储库中编写的代码。

public ClassWithQueryResults {

    @PersistenceContext
    private EntityManager entityManager;

    public void methodWhereYouNeedYourResults() {

        TypedQuery<Department> query = entityManager.createQuery(
                "SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e",
                Department.class);
        List<Department> departments = query.getResultList();

        // ...
    }
}

带有 JPQL、泛型和反射的存储库

采用之前建议的想法,您可以创建一个对所有实体都有效的自定义存储库。第一步是在实体类中创建一个属性,在其中存储应该获取的属性。

public class Department extends AbstractEntity {
    
    public static void String ATTRIBUTE_TO_FETCH = "employees";

    ...
}

通过一些调整,这可以扩展到应获取的所有字段的数组/列表。由于此属性直接存在于您的实体类中,因此出现任何错误和未来努力的可能性很低。显然,此属性在所有实体中都应具有相同的名称。

下一步是创建存储库。我提供了该findAll()方法的示例。您只需将您想要拥有的实体的类名传递给它,其余的由泛型和反射完成。(考虑你想对异常做什么。)

public <T> List<T> findAll(Class<T> tClass) 
        throws NoSuchFieldException, IllegalAccessException {

    String className = tClass.getSimpleName();

    String attributeToFetch = (String) 
    tClass.getDeclaredField("ATTRIBUTE_TO_FETCH").get(null);

    String queryString = String.format("SELECT DISTINCT p FROM %s p LEFT JOIN p.%s c", 
            className, attributeToFetch);

    TypedQuery<T> query = entityManager.createQuery(queryString, tClass);

    return query.getResultList();
}

根据您希望如何实现这一点,通过简单操作字符串来修改/生成查询可以提供 SQL 注入攻击的可能性。

于 2020-10-16T17:34:50.533 回答