FetchMode 如何在 Spring Data JPA 中工作
How does the FetchMode work in Spring Data JPA
我的项目中的三个模型对象之间确实存在关系(post 末尾的模型和存储库片段)。
当我调用 PlaceRepository.findById
时,它会触发三个 select 查询:
("sql")
SELECT * FROM place p where id = arg
SELECT * FROM user u where u.id = place.user.id
SELECT * FROM city c LEFT OUTER JOIN state s on c.woj_id = s.id where c.id = place.city.id
这是相当不寻常的行为(对我而言)。据我在阅读 Hibernate 文档后所知,它应该始终使用 JOIN 查询。在Place
class中FetchType.LAZY
改为FetchType.EAGER
时查询没有区别(查询加SELECT),[=23也是一样=] class 当 FetchType.LAZY
更改为 FetchType.EAGER
(使用 JOIN 查询)。
当我使用 CityRepository.findById
压制时会开火两次 select:
SELECT * FROM city c where id = arg
SELECT * FROM state s where id = city.state.id
我的目标是在所有情况下都具有相同的行为(总是 JOIN 或 SELECT,但首选 JOIN)。
模型定义:
地点:
@Entity
@Table(name = "place")
public class Place extends Identified {
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_user_author")
private User author;
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "area_city_id")
private City city;
//getters and setters
}
城市:
@Entity
@Table(name = "area_city")
public class City extends Identified {
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "area_woj_id")
private State state;
//getters and setters
}
存储库:
PlaceRepository
public interface PlaceRepository extends JpaRepository<Place, Long>, PlaceRepositoryCustom {
Place findById(int id);
}
用户存储库:
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findAll();
User findById(int id);
}
城市资料库:
public interface CityRepository extends JpaRepository<City, Long>, CityRepositoryCustom {
City findById(int id);
}
我认为 Spring Data 忽略了 FetchMode。在使用 Spring Data
时,我总是使用 @NamedEntityGraph
和 @EntityGraph
注释
@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {
// default fetch mode is lazy.
@ManyToMany
List<GroupMember> members = new ArrayList<GroupMember>();
…
}
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
@EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
GroupInfo getByGroupName(String name);
}
查看文档here
首先,@Fetch(FetchMode.JOIN)
和@ManyToOne(fetch = FetchType.LAZY)
是对立的,因为@Fetch(FetchMode.JOIN)
相当于JPAFetchType.EAGER
。
预取很少是一个好的选择,对于可预测的行为,您最好使用查询时 JOIN FETCH
指令:
public interface PlaceRepository extends JpaRepository<Place, Long>, PlaceRepositoryCustom {
@Query(value = "SELECT p FROM Place p LEFT JOIN FETCH p.author LEFT JOIN FETCH p.city c LEFT JOIN FETCH c.state where p.id = :id")
Place findById(@Param("id") int id);
}
public interface CityRepository extends JpaRepository<City, Long>, CityRepositoryCustom {
@Query(value = "SELECT c FROM City c LEFT JOIN FETCH c.state where c.id = :id")
City findById(@Param("id") int id);
}
"FetchType.LAZY
" 只会为主要 table 触发。如果在您的代码中调用任何其他具有 parent table 依赖项的方法,那么它将触发查询以获取该 table 信息。 (多次发射 SELECT)
"FetchType.EAGER
" 将直接创建所有 table 的连接,包括相关的 parent table。 (使用 JOIN
)
何时使用:
假设你强制需要使用依赖 parent table 信息然后选择 FetchType.EAGER
。
如果您只需要某些记录的信息,请使用 FetchType.LAZY
.
请记住,FetchType.LAZY
在代码中选择检索 parent table 信息的位置需要一个活动的数据库会话工厂。
例如对于 LAZY
:
.. Place fetched from db from your dao loayer
.. only place table information retrieved
.. some code
.. getCity() method called... Here db request will be fired to get city table info
Spring-jpa 使用实体管理器创建查询,如果查询是由实体管理器构建的,Hibernate 将忽略获取模式。
以下是我使用的解决方法:
Implement a custom repository which inherits from SimpleJpaRepository
覆盖方法getQuery(Specification<T> spec, Sort sort)
:
@Override
protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = builder.createQuery(getDomainClass());
Root<T> root = applySpecificationToCriteria(spec, query);
query.select(root);
applyFetchMode(root);
if (sort != null) {
query.orderBy(toOrders(sort, root, builder));
}
return applyRepositoryMethodMetadata(entityManager.createQuery(query));
}
在方法中间,添加applyFetchMode(root);
以应用fetch模式,使Hibernate创建具有正确连接的查询。
(不幸的是,我们需要从基 class 复制整个方法和相关的私有方法,因为没有其他扩展点。)
实施applyFetchMode
:
private void applyFetchMode(Root<T> root) {
for (Field field : getDomainClass().getDeclaredFields()) {
Fetch fetch = field.getAnnotation(Fetch.class);
if (fetch != null && fetch.value() == FetchMode.JOIN) {
root.fetch(field.getName(), JoinType.LEFT);
}
}
}
我详细阐述了 dream83619 答案,以使其处理嵌套的 Hibernate @Fetch
注释。我使用递归方法在嵌套关联 类.
中查找注释
So you have to implement custom repository 并覆盖 getQuery(spec, domainClass, sort)
方法。
不幸的是,您还必须复制所有引用的私有方法:(.
这里是代码,省略复制的私有方法
编辑: 添加了剩余的私有方法。
@NoRepositoryBean
public class EntityGraphRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
private final EntityManager em;
protected JpaEntityInformation<T, ?> entityInformation;
public EntityGraphRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.em = entityManager;
this.entityInformation = entityInformation;
}
@Override
protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<S> query = builder.createQuery(domainClass);
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
query.select(root);
applyFetchMode(root);
if (sort != null) {
query.orderBy(toOrders(sort, root, builder));
}
return applyRepositoryMethodMetadata(em.createQuery(query));
}
private Map<String, Join<?, ?>> joinCache;
private void applyFetchMode(Root<? extends T> root) {
joinCache = new HashMap<>();
applyFetchMode(root, getDomainClass(), "");
}
private void applyFetchMode(FetchParent<?, ?> root, Class<?> clazz, String path) {
for (Field field : clazz.getDeclaredFields()) {
Fetch fetch = field.getAnnotation(Fetch.class);
if (fetch != null && fetch.value() == FetchMode.JOIN) {
FetchParent<?, ?> descent = root.fetch(field.getName(), JoinType.LEFT);
String fieldPath = path + "." + field.getName();
joinCache.put(path, (Join) descent);
applyFetchMode(descent, field.getType(), fieldPath);
}
}
}
/**
* Applies the given {@link Specification} to the given {@link CriteriaQuery}.
*
* @param spec can be {@literal null}.
* @param domainClass must not be {@literal null}.
* @param query must not be {@literal null}.
* @return
*/
private <S, U extends T> Root<U> applySpecificationToCriteria(Specification<U> spec, Class<U> domainClass,
CriteriaQuery<S> query) {
Assert.notNull(query);
Assert.notNull(domainClass);
Root<U> root = query.from(domainClass);
if (spec == null) {
return root;
}
CriteriaBuilder builder = em.getCriteriaBuilder();
Predicate predicate = spec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
}
return root;
}
private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {
if (getRepositoryMethodMetadata() == null) {
return query;
}
LockModeType type = getRepositoryMethodMetadata().getLockModeType();
TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type);
applyQueryHints(toReturn);
return toReturn;
}
private void applyQueryHints(Query query) {
for (Map.Entry<String, Object> hint : getQueryHints().entrySet()) {
query.setHint(hint.getKey(), hint.getValue());
}
}
public Class<T> getEntityType() {
return entityInformation.getJavaType();
}
public EntityManager getEm() {
return em;
}
}
http://jdpgrailsdev.github.io/blog/2014/09/09/spring_data_hibernate_join.html
从这个 link:
如果您在 Hibernate 之上使用 JPA,则无法将 Hibernate 使用的 FetchMode 设置为 JOIN但是,如果您在 Hibernate 之上使用 JPA,则无法设置 Hibernate 使用的 FetchMode加入。
Spring Data JPA 库提供了域驱动设计规范API,允许您控制生成的查询的行为。
final long userId = 1;
final Specification<User> spec = new Specification<User>() {
@Override
public Predicate toPredicate(final Root<User> root, final
CriteriaQuery<?> query, final CriteriaBuilder cb) {
query.distinct(true);
root.fetch("permissions", JoinType.LEFT);
return cb.equal(root.get("id"), userId);
}
};
List<User> users = userRepository.findAll(spec);
根据 Vlad Mihalcea(参见 https://vladmihalcea.com/hibernate-facts-the-importance-of-fetch-strategy/):
JPQL queries may override the default fetching strategy. If we don’t
explicitly declare what we want to fetch using inner or left join
fetch directives, the default select fetch policy is applied.
似乎 JPQL 查询可能会覆盖您声明的获取策略,因此您必须使用 join fetch
来急切地加载一些引用的实体或简单地使用 EntityManager 按 id 加载(这将遵循您的获取策略但可能不是您用例的解决方案)。
获取模式仅在select通过 id 获取对象 时有效,即使用 entityManager.find()
。由于 Spring 数据将始终创建查询,因此获取模式配置对您没有用处。您可以使用带有提取连接的专用查询或使用实体图。
当您想要获得最佳性能时,您应该 select 仅使用您真正需要的数据子集。为此,通常建议使用 DTO 方法来避免获取不必要的数据,但这通常会导致大量容易出错的样板代码,因为您需要定义一个专用查询来通过 JPQL 构建 DTO 模型构造函数表达式。
Spring 数据投影在这里可以提供帮助,但在某些时候您将需要一个像 Blaze-Persistence Entity Views 这样的解决方案,它使这变得非常简单,并且在它的套筒中有更多的功能可以派上用场!您只需为每个实体创建一个 DTO 接口,其中的 getter 代表您需要的数据子集。您的问题的解决方案可能如下所示
@EntityView(Identified.class)
public interface IdentifiedView {
@IdMapping
Integer getId();
}
@EntityView(Identified.class)
public interface UserView extends IdentifiedView {
String getName();
}
@EntityView(Identified.class)
public interface StateView extends IdentifiedView {
String getName();
}
@EntityView(Place.class)
public interface PlaceView extends IdentifiedView {
UserView getAuthor();
CityView getCity();
}
@EntityView(City.class)
public interface CityView extends IdentifiedView {
StateView getState();
}
public interface PlaceRepository extends JpaRepository<Place, Long>, PlaceRepositoryCustom {
PlaceView findById(int id);
}
public interface UserRepository extends JpaRepository<User, Long> {
List<UserView> findAllByOrderByIdAsc();
UserView findById(int id);
}
public interface CityRepository extends JpaRepository<City, Long>, CityRepositoryCustom {
CityView findById(int id);
}
免责声明,我是 Blaze-Persistence 的作者,所以我可能有偏见。
spring data jpa 使用的实体管理器忽略了获取模式。
在 Repository 方法上使用 @EntityGraph 注释,
@EntityGraph(attributePaths = { "user", "hashtags"})
Page<LIPost> findByVoteTypeIn(Set<VoteType> listOfVotetype, Pageable paging);
这里的 user 和 hashtags 是 LIPost 实体中的属性。
由spring数据JPA构建的查询使用左外连接获取相关实体(用户和主题标签)数据。
在这种情况下,无需在实体 class.
上使用注解 @NamedEntityGraph
我的项目中的三个模型对象之间确实存在关系(post 末尾的模型和存储库片段)。
当我调用 PlaceRepository.findById
时,它会触发三个 select 查询:
("sql")
SELECT * FROM place p where id = arg
SELECT * FROM user u where u.id = place.user.id
SELECT * FROM city c LEFT OUTER JOIN state s on c.woj_id = s.id where c.id = place.city.id
这是相当不寻常的行为(对我而言)。据我在阅读 Hibernate 文档后所知,它应该始终使用 JOIN 查询。在Place
class中FetchType.LAZY
改为FetchType.EAGER
时查询没有区别(查询加SELECT),[=23也是一样=] class 当 FetchType.LAZY
更改为 FetchType.EAGER
(使用 JOIN 查询)。
当我使用 CityRepository.findById
压制时会开火两次 select:
SELECT * FROM city c where id = arg
SELECT * FROM state s where id = city.state.id
我的目标是在所有情况下都具有相同的行为(总是 JOIN 或 SELECT,但首选 JOIN)。
模型定义:
地点:
@Entity
@Table(name = "place")
public class Place extends Identified {
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_user_author")
private User author;
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "area_city_id")
private City city;
//getters and setters
}
城市:
@Entity
@Table(name = "area_city")
public class City extends Identified {
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "area_woj_id")
private State state;
//getters and setters
}
存储库:
PlaceRepository
public interface PlaceRepository extends JpaRepository<Place, Long>, PlaceRepositoryCustom {
Place findById(int id);
}
用户存储库:
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findAll();
User findById(int id);
}
城市资料库:
public interface CityRepository extends JpaRepository<City, Long>, CityRepositoryCustom {
City findById(int id);
}
我认为 Spring Data 忽略了 FetchMode。在使用 Spring Data
时,我总是使用@NamedEntityGraph
和 @EntityGraph
注释
@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {
// default fetch mode is lazy.
@ManyToMany
List<GroupMember> members = new ArrayList<GroupMember>();
…
}
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
@EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
GroupInfo getByGroupName(String name);
}
查看文档here
首先,@Fetch(FetchMode.JOIN)
和@ManyToOne(fetch = FetchType.LAZY)
是对立的,因为@Fetch(FetchMode.JOIN)
相当于JPAFetchType.EAGER
。
预取很少是一个好的选择,对于可预测的行为,您最好使用查询时 JOIN FETCH
指令:
public interface PlaceRepository extends JpaRepository<Place, Long>, PlaceRepositoryCustom {
@Query(value = "SELECT p FROM Place p LEFT JOIN FETCH p.author LEFT JOIN FETCH p.city c LEFT JOIN FETCH c.state where p.id = :id")
Place findById(@Param("id") int id);
}
public interface CityRepository extends JpaRepository<City, Long>, CityRepositoryCustom {
@Query(value = "SELECT c FROM City c LEFT JOIN FETCH c.state where c.id = :id")
City findById(@Param("id") int id);
}
"FetchType.LAZY
" 只会为主要 table 触发。如果在您的代码中调用任何其他具有 parent table 依赖项的方法,那么它将触发查询以获取该 table 信息。 (多次发射 SELECT)
"FetchType.EAGER
" 将直接创建所有 table 的连接,包括相关的 parent table。 (使用 JOIN
)
何时使用:
假设你强制需要使用依赖 parent table 信息然后选择 FetchType.EAGER
。
如果您只需要某些记录的信息,请使用 FetchType.LAZY
.
请记住,FetchType.LAZY
在代码中选择检索 parent table 信息的位置需要一个活动的数据库会话工厂。
例如对于 LAZY
:
.. Place fetched from db from your dao loayer
.. only place table information retrieved
.. some code
.. getCity() method called... Here db request will be fired to get city table info
Spring-jpa 使用实体管理器创建查询,如果查询是由实体管理器构建的,Hibernate 将忽略获取模式。
以下是我使用的解决方法:
Implement a custom repository which inherits from SimpleJpaRepository
覆盖方法
getQuery(Specification<T> spec, Sort sort)
:@Override protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<T> query = builder.createQuery(getDomainClass()); Root<T> root = applySpecificationToCriteria(spec, query); query.select(root); applyFetchMode(root); if (sort != null) { query.orderBy(toOrders(sort, root, builder)); } return applyRepositoryMethodMetadata(entityManager.createQuery(query)); }
在方法中间,添加
applyFetchMode(root);
以应用fetch模式,使Hibernate创建具有正确连接的查询。(不幸的是,我们需要从基 class 复制整个方法和相关的私有方法,因为没有其他扩展点。)
实施
applyFetchMode
:private void applyFetchMode(Root<T> root) { for (Field field : getDomainClass().getDeclaredFields()) { Fetch fetch = field.getAnnotation(Fetch.class); if (fetch != null && fetch.value() == FetchMode.JOIN) { root.fetch(field.getName(), JoinType.LEFT); } } }
我详细阐述了 dream83619 答案,以使其处理嵌套的 Hibernate @Fetch
注释。我使用递归方法在嵌套关联 类.
So you have to implement custom repository 并覆盖 getQuery(spec, domainClass, sort)
方法。
不幸的是,您还必须复制所有引用的私有方法:(.
这里是代码,省略复制的私有方法
编辑: 添加了剩余的私有方法。
@NoRepositoryBean
public class EntityGraphRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
private final EntityManager em;
protected JpaEntityInformation<T, ?> entityInformation;
public EntityGraphRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.em = entityManager;
this.entityInformation = entityInformation;
}
@Override
protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<S> query = builder.createQuery(domainClass);
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
query.select(root);
applyFetchMode(root);
if (sort != null) {
query.orderBy(toOrders(sort, root, builder));
}
return applyRepositoryMethodMetadata(em.createQuery(query));
}
private Map<String, Join<?, ?>> joinCache;
private void applyFetchMode(Root<? extends T> root) {
joinCache = new HashMap<>();
applyFetchMode(root, getDomainClass(), "");
}
private void applyFetchMode(FetchParent<?, ?> root, Class<?> clazz, String path) {
for (Field field : clazz.getDeclaredFields()) {
Fetch fetch = field.getAnnotation(Fetch.class);
if (fetch != null && fetch.value() == FetchMode.JOIN) {
FetchParent<?, ?> descent = root.fetch(field.getName(), JoinType.LEFT);
String fieldPath = path + "." + field.getName();
joinCache.put(path, (Join) descent);
applyFetchMode(descent, field.getType(), fieldPath);
}
}
}
/**
* Applies the given {@link Specification} to the given {@link CriteriaQuery}.
*
* @param spec can be {@literal null}.
* @param domainClass must not be {@literal null}.
* @param query must not be {@literal null}.
* @return
*/
private <S, U extends T> Root<U> applySpecificationToCriteria(Specification<U> spec, Class<U> domainClass,
CriteriaQuery<S> query) {
Assert.notNull(query);
Assert.notNull(domainClass);
Root<U> root = query.from(domainClass);
if (spec == null) {
return root;
}
CriteriaBuilder builder = em.getCriteriaBuilder();
Predicate predicate = spec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
}
return root;
}
private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {
if (getRepositoryMethodMetadata() == null) {
return query;
}
LockModeType type = getRepositoryMethodMetadata().getLockModeType();
TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type);
applyQueryHints(toReturn);
return toReturn;
}
private void applyQueryHints(Query query) {
for (Map.Entry<String, Object> hint : getQueryHints().entrySet()) {
query.setHint(hint.getKey(), hint.getValue());
}
}
public Class<T> getEntityType() {
return entityInformation.getJavaType();
}
public EntityManager getEm() {
return em;
}
}
http://jdpgrailsdev.github.io/blog/2014/09/09/spring_data_hibernate_join.html
从这个 link:
如果您在 Hibernate 之上使用 JPA,则无法将 Hibernate 使用的 FetchMode 设置为 JOIN但是,如果您在 Hibernate 之上使用 JPA,则无法设置 Hibernate 使用的 FetchMode加入。
Spring Data JPA 库提供了域驱动设计规范API,允许您控制生成的查询的行为。
final long userId = 1;
final Specification<User> spec = new Specification<User>() {
@Override
public Predicate toPredicate(final Root<User> root, final
CriteriaQuery<?> query, final CriteriaBuilder cb) {
query.distinct(true);
root.fetch("permissions", JoinType.LEFT);
return cb.equal(root.get("id"), userId);
}
};
List<User> users = userRepository.findAll(spec);
根据 Vlad Mihalcea(参见 https://vladmihalcea.com/hibernate-facts-the-importance-of-fetch-strategy/):
JPQL queries may override the default fetching strategy. If we don’t explicitly declare what we want to fetch using inner or left join fetch directives, the default select fetch policy is applied.
似乎 JPQL 查询可能会覆盖您声明的获取策略,因此您必须使用 join fetch
来急切地加载一些引用的实体或简单地使用 EntityManager 按 id 加载(这将遵循您的获取策略但可能不是您用例的解决方案)。
获取模式仅在select通过 id 获取对象 时有效,即使用 entityManager.find()
。由于 Spring 数据将始终创建查询,因此获取模式配置对您没有用处。您可以使用带有提取连接的专用查询或使用实体图。
当您想要获得最佳性能时,您应该 select 仅使用您真正需要的数据子集。为此,通常建议使用 DTO 方法来避免获取不必要的数据,但这通常会导致大量容易出错的样板代码,因为您需要定义一个专用查询来通过 JPQL 构建 DTO 模型构造函数表达式。
Spring 数据投影在这里可以提供帮助,但在某些时候您将需要一个像 Blaze-Persistence Entity Views 这样的解决方案,它使这变得非常简单,并且在它的套筒中有更多的功能可以派上用场!您只需为每个实体创建一个 DTO 接口,其中的 getter 代表您需要的数据子集。您的问题的解决方案可能如下所示
@EntityView(Identified.class)
public interface IdentifiedView {
@IdMapping
Integer getId();
}
@EntityView(Identified.class)
public interface UserView extends IdentifiedView {
String getName();
}
@EntityView(Identified.class)
public interface StateView extends IdentifiedView {
String getName();
}
@EntityView(Place.class)
public interface PlaceView extends IdentifiedView {
UserView getAuthor();
CityView getCity();
}
@EntityView(City.class)
public interface CityView extends IdentifiedView {
StateView getState();
}
public interface PlaceRepository extends JpaRepository<Place, Long>, PlaceRepositoryCustom {
PlaceView findById(int id);
}
public interface UserRepository extends JpaRepository<User, Long> {
List<UserView> findAllByOrderByIdAsc();
UserView findById(int id);
}
public interface CityRepository extends JpaRepository<City, Long>, CityRepositoryCustom {
CityView findById(int id);
}
免责声明,我是 Blaze-Persistence 的作者,所以我可能有偏见。
spring data jpa 使用的实体管理器忽略了获取模式。
在 Repository 方法上使用 @EntityGraph 注释,
@EntityGraph(attributePaths = { "user", "hashtags"})
Page<LIPost> findByVoteTypeIn(Set<VoteType> listOfVotetype, Pageable paging);
这里的 user 和 hashtags 是 LIPost 实体中的属性。
由spring数据JPA构建的查询使用左外连接获取相关实体(用户和主题标签)数据。
在这种情况下,无需在实体 class.
上使用注解 @NamedEntityGraph