懒加载与规范不起作用

huangapple go评论61阅读模式
英文:

Lazy loading not working with Specification

问题

已经迁移到Spring v2.1.2.RELEASE,并使用Hibernate版本5.3.7.FINAL。

我们在实体之间有一个@OneToOne双向关系,如下所示:

class Parent {
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "parent", optional = false)
    @NotAudited
    private Child child;
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

class Child {
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    @MapsId
    @NotNull
    private Parent parent;
    
    @Id
    @Column(name = "indent_id", insertable = false, updatable = false)
    private Integer parentId;
}

调用:

parentRepository.findByIdIn(Collections.singletonList(1));

会生成以下数据库调用:

Hibernate:
select parent0_.id as id1_19_ from parent parent0_ where parent0_.id in (?)

这个部分工作正常,现在考虑下面的用例:

public class Filter implements Specification<Parent> {
    
    @Override
    public Predicate toPredicate(Root<Indent> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        // TODO Auto-generated method stub
        List<Integer> parentIdList = new ArrayList<>();
        parentIdList.add(1);
        ArrayList<Predicate> predicates = new ArrayList<>();
        predicates.add(root.get("id").in(parentIdList));
        return predicates.size() == 0 ? null
                : criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
    }
} 

PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.Direction.DESC, "id");
Filter filter = new Filter();
Page<Indent> indentPage = indentRepository.findAll(filter, pageRequest);

会生成对Child实体的数据库调用:

Hibernate:
select parent0_.id as id1_19_ from parent parent0_ where parent0_.id in (?)
select child0_.parent_id as parent_i1_29_ from child child0_ where child0_.indent_id in (?)

有没有办法避免在这里对Child实体进行数据库调用?

英文:

I have migrated to spring v2.1.2.RELEASE and using hibernate version 5.3.7.FINAL

We have @OneToOne bi directional relationship between entity as follows

class Parent{

    @OneToOne(fetch = FetchType.LAZY, mappedBy = &quot;parent&quot;, optional = false)
    @NotAudited
    private Child child; 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

class Child{
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;parent_id&quot;)
    @MapsId
    @NotNull
    private Parent parent;

    @Id
    @Column(name = &quot;indent_id&quot;, insertable = false, updatable = false)
    private Integer parentId;

}

calling

parentRepository.findByIdIn(Collections.singletonList(1));

makes db call as:
Hibernate:

select parent0_.id as id1_19_ from  parent parent0_ where parent0_.id in (?)

Which is working fine, now consider use case,

public class Filter implements Specification&lt;Parent&gt; {

		@Override
		public Predicate toPredicate(Root&lt;Indent&gt; root, CriteriaQuery&lt;?&gt; query, CriteriaBuilder criteriaBuilder) {
			// TODO Auto-generated method stub
			List&lt;Integer&gt; parentIdList = new ArrayList&lt;&gt;();
			parentIdList.add(1);
			ArrayList&lt;Predicate&gt; predicates = new ArrayList&lt;&gt;();
			predicates.add(root.get(&quot;id&quot;).in(parentIdList));
			return predicates.size() == 0 ? null
					: criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));

		}

	} 



    PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.Direction.DESC, &quot;id&quot;);
	Filter filter = new Filter();
	Page&lt;Indent&gt; indentPage = indentRepository.findAll(filter, pageRequest);

makes DB call to Child entity as well

Hibernate:
select parent0_.id as id1_19_ from  parent parent0_ where parent0_.id in (?)
select child0_.parent_id as parent_i1_29_ from child child0_ where child0_.indent_id in (?)

Is there any way to avoid Db call to child entity here?

答案1

得分: 1

可以定义一个实体图来指定一个模式,该模式可以传递给查询,以确定要获取哪些属性。图中未包含的属性将被持久化提供程序视为延迟加载(LAZY)。

@Entity
@NamedEntityGraph(name = "only_id", attributeNodes = {@NamedAttributeNode("id")})
class Parent {
    
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "parent", optional = false)
    @NotAudited
    private Child child; 
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

JPA API:

EntityGraph entityGraph = entityManager.getEntityGraph("only_id");
// 定义你的查询
query.setHint("javax.persistence.fetchgraph", graph);

更多详情:https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm

使用 Spring Data JPA:

public interface MyRepository extends JpaRepository<Parent, Integer> {
    
    @EntityGraph(value = "only_id")
    Parent findById(Integer id);
}

更多详情:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.entity-graph

JpaSpecificationExecutor:

public interface MyRepository extends JpaSpecificationExecutor<Parent> {
    
    @Override
    @EntityGraph(attributePaths = {"only_id"}, type = EntityGraphType.FETCH)
    Parent findOne(Specification<Parent> spec);
}

更多详情:https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html

英文:

You can define an entity graph to specify a pattern that can be passed to a query to determine which attributes you want to be fetched. Attributes that are not included in the graph will be treated as LAZY by persistence provider.

    @Entity
    @NamedEntityGraph(name = &quot;only_id&quot;, attributeNodes={@NamedAttributeNode(&quot;id&quot;)})
    class Parent{
    
        @OneToOne(fetch = FetchType.LAZY, mappedBy = &quot;parent&quot;, optional = false)
        @NotAudited
        private Child child; 
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    }

JPA API:

EntityGraph entityGraph = entityManager.getEntityGraph(&quot;only_idh&quot;);
// define your query
query.setHint(&quot;javax.persistence.fetchgraph&quot;,graph);

More details : https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm

With Spring Data JPA

 public interface MyRepository extends JpaRepository&lt;Parent, Integer&gt; {
 
    @EntityGraph(value = &quot;only id&quot;)
    Parent findById(Integer id)

More details : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.entity-graph

JpaSpecificationExecutor :

public interface MyRepository extends JpaSpecificationExecutor&lt;Parent&gt; {
   @Override
   @EntityGraph(attributePaths = {&quot;only_id&quot;}, type=EntityGraphType.FETCH)
   Parent findByOne(Specification&lt;Parent&gt; spec);
}

More details : https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html

答案2

得分: 0

JPA提供程序通常将fetch类型设置为Lazy作为提示,因此在您的情况下可能根本不考虑它。请查看以下内容:

fetch public abstract FetchType fetch(可选)定义字段或属性的值是否应该懒加载还是必须立即提取。EAGER策略是对持久性提供程序运行时的要求,必须立即提取值。LAZY策略是对持久性提供程序运行时的提示。如果未指定,默认为EAGER。默认值:EAGER

JPA文档

英文:

JPA provider usually takes fetch type Lazy as a hint, so may be in your case its not considering it at all. check below

> fetch public abstract FetchType fetch (Optional) Defines whether the
> value of the field or property should be lazily loaded or must be
> eagerly fetched. The EAGER strategy is a requirement on the
> persistence provider runtime that the value must be eagerly fetched.
> The LAZY strategy is a hint to the persistence provider runtime. If
> not specified, defaults to EAGER. Default: EAGER

JPA Doc

huangapple
  • 本文由 发表于 2020年8月8日 20:40:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/63315523.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定