懒惰异常:大小与渴望?

时间:2014-07-18 15:49:05

标签: spring jpa lazy-loading

我面对的是:

failed to lazily initialize a collection of role: ,no session or session was closed

尝试访问(来自控制器或junit)“DataDictionary”中“DataDictionaryEntry”的集合。

数据字典

@Entity
@Table( name = "IDS_RAVE_DATA_DICTIONARY",
        uniqueConstraints={@UniqueConstraint(columnNames={"name"})
})
public class DataDictionary extends UnversionedObject {

    @Column
    private String name;

    @OneToMany(mappedBy="dataDictionary",fetch=FetchType.EAGER)
    private Collection<DataDictionaryEntry> dataDictionaryNames;

    /* constructor */
    public DataDictionary() {
        super();
    }


    /* getters & setters */
}

DataDictionaryEntry

@Entity
@Table( name = "IDS_RAVE_DATA_DICTIONARY_ENTRY",
        uniqueConstraints={@UniqueConstraint(columnNames={"dataDictionary","codedData"})
})
public class DataDictionaryEntry extends UnversionedObject {

    @ManyToOne
    @JoinColumn(name="dataDictionary")
    private DataDictionary dataDictionary;

    @Column
    private String codedData;

    @Column
    private Integer ordinal;

    @Column
    private String userDataString;

    @Column
    private Boolean specify;

    /* constructor */
    public DataDictionaryEntry() {
        super();
    }

    /* getters & setters */
}

我有一个抽象的服务对象和另一个扩展它的服务:

通用服务

@Transactional
public abstract class RaveGeneralServiceImpl<T> implements RaveGeneralService<T> {

    private JpaRepository<T, Long> repo;

    /**
     * Init the general rave services with your specific repo
     * @param repo
     */
    protected void init(JpaRepository<T, Long> repo){
        this.repo = repo;
    }

    @Override
    public List<T> findAll(){
         return repo.findAll();
    }

    @Override
    public T save(T obj){
         return repo.save(obj);
     }

    @Override
    public void flush(){
        repo.flush();
    }

}

DataDictionaryServiceImpl

@Service
public class DataDictionaryServiceImpl extends RaveGeneralServiceImpl<DataDictionary> implements DataDictionaryService {
    @Resource
    private DataDictionaryRepository dataDictionaryRepository;

    @PostConstruct
    public void init() {
        super.init(dataDictionaryRepository);
    }

}

我可以找到有关如何解决它的回复。经常看到的第一个解决方案是将LAZY更改为EAGER。当我在访问FINDALL()方法时打印生成的查询时,它显示以下内容:

Hibernate: 
    /* select
        generatedAlias0 
    from
        DataDictionary as generatedAlias0 */ select
            datadictio0_.ID as ID81_,
            datadictio0_.createdByUser as createdB2_81_,
            datadictio0_.createdTime as createdT3_81_,
            datadictio0_.lastUpdateTime as lastUpda4_81_,
            datadictio0_.lastUpdateUser as lastUpda5_81_,
            datadictio0_.VERSION as VERSION81_,
            datadictio0_.name as name81_ 
        from
            IDS_RAVE_DATA_DICTIONARY datadictio0_
Hibernate: 
    /* load one-to-many com.bdls.ids.model.rave.DataDictionary.dataDictionaryNames */ select
        datadictio0_.dataDictionary as dataDic11_81_1_,
        datadictio0_.ID as ID1_,
        datadictio0_.ID as ID82_0_,
        datadictio0_.createdByUser as createdB2_82_0_,
        datadictio0_.createdTime as createdT3_82_0_,
        datadictio0_.lastUpdateTime as lastUpda4_82_0_,
        datadictio0_.lastUpdateUser as lastUpda5_82_0_,
        datadictio0_.VERSION as VERSION82_0_,
        datadictio0_.codedData as codedData82_0_,
        datadictio0_.dataDictionary as dataDic11_82_0_,
        datadictio0_.ordinal as ordinal82_0_,
        datadictio0_.specify as specify82_0_,
        datadictio0_.userDataString as userDat10_82_0_ 
    from
        IDS_RAVE_DATA_DICTIONARY_ENTRY datadictio0_ 
    where
        datadictio0_.dataDictionary=?

我们经常看到的第二个解决方案是调用正在懒惰地初始化的组件的.size()。所以确实将我的服务改为:

@Service
public class DataDictionaryServiceImpl extends RaveGeneralServiceImpl<DataDictionary> implements DataDictionaryService {
    @Resource
    private DataDictionaryRepository dataDictionaryRepository;

    @PostConstruct
    public void init() {
        super.init(dataDictionaryRepository);
    }

    @Override
    public List<DataDictionary> findAll() {
        List<DataDictionary> results = super.findAll();
        for (DataDictionary dd : results) {
            dd.getDataDictionaryNames().size();// init lazy
        }
        return results;
    }

}

懒惰的异常也消失了!但最终结果是相同的查询......那么如果最终查询是相同的那么保持LAZY的附加价值是什么?或者我做错了吗?

假设对于前端,您将拥有一个仅显示基本信息(例如名称)的数据表,它会调用findAll()但仍会查询该对象的完整依赖项吗?

2 个答案:

答案 0 :(得分:1)

虽然这种方法的结果几乎完全相同,但保持懒惰的价值在于,如果你不需要在其他查询中获取它,你就不会自动获取它。使关系急切应用于访问该实体的每个方法,而在集合上调用大小会强制为该一个实例提取它。

还有其他方法可能更有效,例如在JPA查询本身中使用连接提取限定符,允许提供程序使用单个选择来获取关系。

答案 1 :(得分:0)

您可以使用:Hibernate.initialize()来初始化Lazy集合。

或者使用spring来避免LazyException在web.xml中使用filer:

<filter>
  <filter-name>hibernateFilterChain</filter-name>
  <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>hibernateFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

但请记住,如果您正在考虑良好的应用程序设计和性能,那么使用延迟提取是个坏主意。