Spring Jpa 规范和渴望加载

Posted

技术标签:

【中文标题】Spring Jpa 规范和渴望加载【英文标题】:Spring Jpa Specification and Eager loading 【发布时间】:2016-03-14 16:06:46 【问题描述】:

我如何通过Jpa Specification获得票务列表及其类别

示例模型:

@Entity
@Table(name = "tickets")
public class Ticket 

@Id
private Integer id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category 

服务方式:

public Page<Ticket> findAll(Pageable pageable) 
        return ticketRepository.findAll((root, query, cb) -> 
            root.join("category");
            return query.getRestriction();
        , pageable);

【问题讨论】:

如果你想通过加载门票类别看到这个答案:***.com/questions/15359306/… @Si mo,我找不到分类! @SkorpEN 与 JPA 规范无关。 @AndréOnuki Spring Jpa 规范更多关于 JpaRepository 从链接“Jpa Specification”到 SpringData-jpa。如果您认为应该更改链接,请更改它。无论如何,这个问题是在标签和链接中看到的 spring-data-jpa。 @SkorpEN 我理解这种困惑。问题是关于 JPA 的规范类,而不是 JPA 的规范。这个问题确实是关于 JPA 的,但是您发布的链接与 Specification 类无关。 【参考方案1】:

我能够通过使用 fetch 而不是 join 来预先加载集合。

public Page<Ticket> findAll(Pageable pageable) 
    return ticketRepository.findAll((root, query, cb) -> 
        root.fetch("category");
        return query.getRestriction();
    , pageable);

fetch 方法将使用默认的连接类型(内部)。如果要加载没有类别的票证,则必须将JoinType.LEFT 作为第二个参数传递。

【讨论】:

【参考方案2】:

要加载Lazy 关系,您有一些方法。最常用的一些是:

计算要加载的列表

那是什么?所以,你需要用关系“做点什么”,这样它才能被加载。所以你可以打电话

List<Tiket> tickets = ticketRepository.findAll();
for(Ticket t : tickets)
   t.category.name;

这样,您可以强制 JPA 查询类别。可是等等!这样做的问题是你会有很多额外的查询(在一个大场景中,这可能是一个性能问题)。因此,大多数情况下,最好的解决方案是另一种解决方案。

自定义 JPA 查询

您应该进入存储库接口并创建一个带有@Query 注释的新方法来获取关系。比如:

SELECT t FROM Ticket t JOIN FETCH t.category

【讨论】:

这个答案与JPA规范无关。【参考方案3】:

正如他们所说,*** 给予,*** 接受...... 我很久以前就把这门课关掉了,请随意回收它..

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Hibernate;

public class BeanLoader 
    /**
     * *** safe, if called before json creation, cyclic object must be avoided
     */
    public static void eagerize(Object obj)  
        if(!Hibernate.isInitialized(obj))
            Hibernate.initialize(obj);
        PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
        for (PropertyDescriptor propertyDescriptor : properties) 
            Object origProp = null;
            try 
                origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
             catch (IllegalAccessException e) 
                // Handled, but hopefully dead code  
                origProp=null;
             catch (InvocationTargetException e) 
                // Single catch for obsolete java developers!
                origProp=null;
             catch (NoSuchMethodException e) 
                // Single catch for obsolete java developers!
                origProp=null;
            
            if (origProp != null 
                    && origProp.getClass().getPackage().toString().contains("domain")) 
                eagerize(origProp, new ArrayList<String>());
            
            if (origProp instanceof Collection)                
                for (Object item : (Collection) origProp) 
                    if (item.getClass().getPackage().toString().contains("domain"))
                        eagerize(item, new ArrayList<String>());
                    
                
            
        
    

    /**
     * ***s if passed a bean containing cyclic fields. Call only if sure that this won't happen!
     */
    public static void eagerizeUnsafe(Object obj)  
        if(!Hibernate.isInitialized(obj))
            Hibernate.initialize(obj);
        PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
        for (PropertyDescriptor propertyDescriptor : properties) 
            Object origProp = null;
            try 
                origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
             catch (IllegalAccessException e) 
                // Handled, but hopefully dead code  
                origProp=null;
             catch (InvocationTargetException e) 
                // Single catch for obsolete java developers!
                origProp=null;
             catch (NoSuchMethodException e) 
                // Single catch for obsolete java developers!
                origProp=null;
            
            if (origProp != null 
                    && origProp.getClass().getPackage().toString().contains("domain")) 
                eagerize(origProp);
            
            if (origProp instanceof Collection)                
                for (Object item : (Collection) origProp) 
                    if (item.getClass().getPackage().toString().contains("domain"))
                        eagerize(item);
                    
                
            
        
    

    private static void eagerize(Object obj, ArrayList<String> visitedBeans)  
        if (!visitedBeans.contains(obj.getClass().getName()))
            visitedBeans.add(obj.getClass().getName());
         else 
            return;
        
        if(!Hibernate.isInitialized(obj))
            Hibernate.initialize(obj);
        PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
        for (PropertyDescriptor propertyDescriptor : properties) 
            Object origProp = null;
            try 
                origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
             catch (IllegalAccessException e) 
                // Handled, but hopefully dead code  
                origProp=null;
             catch (InvocationTargetException e) 
                // Single catch for obsolete java developers!
                origProp=null;
             catch (NoSuchMethodException e) 
                // Single catch for obsolete java developers!
                origProp=null;
            
            if (origProp != null 
                    && origProp.getClass().getPackage().toString().contains("domain")) 
                eagerize(origProp, visitedBeans);
            
            if (origProp instanceof User)
                ((User) origProp).setRelatedNra(null);
                User u=(User) origProp;
                if (u.getRelatedMps()!=null)
                    u.getRelatedMps().clear();
                if (u.getRelatedDps()!=null)
                    u.getRelatedDps().clear();
            
            if (origProp instanceof Collection)                
                for (Object item : (Collection) origProp) 
                    if (item.getClass().getPackage().toString().contains("domain"))
                        eagerize(item, (ArrayList<String>) visitedBeans.clone());
                    
                
            
        
    

根据需要对其进行修改。您将调用的方法是“eagerizeUnsafe”。 YMMV,但这应该是让延迟初始化 bean 的所有集合的技巧。

【讨论】:

以上是关于Spring Jpa 规范和渴望加载的主要内容,如果未能解决你的问题,请参考以下文章

如何在 spring data jpa 中使用预测和规范?

Spring JPA配置讲解

Spring Data JPA - 规范和 Querydsl

干货|一文读懂 Spring Data Jpa!

Spring Data JPA 整合Spring

使用连接表存储库的@manytomany 中的 Spring 数据 jpa 规范和可分页