Hibernate Lazy Load是递归加载Set或Referencing数据

时间:2014-09-15 21:34:26

标签: java spring hibernate recursion lazy-loading

我对Hibenate延迟加载有一些奇怪的问题。我有2个实体ProcessEntity和SectionEntity。 ProcessEntity可以有许多SectionEntity,而SectionEntity应该知道它属于哪个ProcessEntity(@OneToMany)。我遇到的问题是当我使用hibernateTemplate.get(id)加载ProcessEntity然后调用我的自定义函数fetchLazyCollections(entity, ...)时,它会循环实体方法,直到找到PersistentCollection,然后强制对该方法进行延迟加载。

当ProcessEntity的Set lazy加载时,它会递归加载SectionEntity中的所有数据,这意味着它会加载ProcessEntity,它会加载加载ProcessEntity的SectionEntity,依此类推!当我尝试序列化数据时,这会导致堆栈溢出,并且必须对性能造成严重影响。当我进行HQL查询等时,这不会发生。

以下是我对实体的设置:

ProcessEntity:

@javax.persistence.Entity(name = "processes")
public class ProcessEntity extends Entity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id = Entity.UNSAVED_ID;

    ...

    @OneToMany(fetch=FetchType.LAZY, mappedBy="process")
    private Set<SectionEntity> sections = new HashSet<SectionEntity>();

    ...

    public Set<SectionEntity> getSections() {
        return sections;
    }
    public void setSections(Set<SectionEntity> sections) {
        this.sections = sections;
    }

    ...
}

SectionEntity:

@javax.persistence.Entity(name = "sections")
public class SectionEntity extends Entity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id = Entity.UNSAVED_ID;

    ...

    @ManyToOne(fetch = FetchType.LAZY, targetEntity = ProcessEntity.class)
    @JoinColumn(name="process", referencedColumnName="id")
    private ProcessEntity process;

    @Override
    public int getId() {
        return this.id;
    }
    public void setId(int id) {
        this.id = id;
    }

    ...

    public ProcessEntity getProcess() {
        return process;
    }
    public void setProcess(ProcessEntity process) {
        this.process = process;
    }

    ...
}

fetchLazyCollections:

public <E extends Entity> E fetchLazyCollections(E entity, String... specifiedCollections) {
    if(getCurrentSession() == null) {
        throw new SessionException("No session found for fetching collections.");
    }

    // Fetch the collections using reflection
    Class<? extends Entity> clazz = entity.getClass();
    for(Method method : clazz.getMethods()) {
        Class<?> returnType = method.getReturnType();
        if(ReflectUtils.isClassCollection(returnType)) {

            // Check if the collection type is specified via the getter
            List<String> specified = Arrays.asList(specifiedCollections);
            if(!specified.isEmpty()) {
                if(!specified.contains(method.getName())) {
                    continue;
                }
            }

            try {
                // Check that the collection is persistent
                Collection collection = (Collection) method.invoke(entity);
                if(collection instanceof PersistentCollection) {
                    collection.size(); // invokes lazy loading
                }
            }
            catch (IllegalAccessException | IllegalArgumentException
                    | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    return entity;
}

我在后端使用Spring来使用@Transactional。这是我的Hibernate弹簧模块(@Configuration):

@Configuration
@Import({
    MappingModule.class
})
@ImportResource("classpath:nz/co/doltech/ims/properties.xml")
@EnableTransactionManagement
public class HibernateModule {

    private static int statisticId = 0;

    private @Value("#{app['root.path']}") String projectPath;
    private @Value("#{app['database.jndiname']}") String databaseJndiName;

    private @Value("#{app['hibernate.dialect']}") String hibernateDialect;
    private @Value("#{app['hibernate.hbm2ddl']}") String hibernateHbm2dll;
    private @Value("#{app['hibernate.show_sql']}") String hibernateShowSql;
    private @Value("#{app['hibernate.format_sql']}") String hibernateFormatSql;
    private @Value("#{app['hibernate.generate_statistics']}") String hibarnateStatistics;
    private @Value("#{app['hibernate.cache.provider_class']}") String hibarnateCacheProviderClass;
    private @Value("#{app['hibernate.cache.use_query_cache']}") String hibarnateQueryCache;
    private @Value("#{app['hibernate.cache.use_second_level_cache']}") String hibarnateSecondLevelCache;
    private @Value("#{app['hibernate.cache.use_structured_entries']}") String hibernateStructuredEntries;

    private @Value("#{app['net.sf.ehcache.configurationResourceName']}") String hibernateEhcacheResource;

    private @Value("#{app['flyway.enabled']}") String flywayEnabled;
    private @Value("#{app['flyway.basePath']}") String flywayBasePath;

    @Bean(name="dataSource")
    public JndiObjectFactoryBean getDriverManagerDataSource() {
        JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();

        dataSource.setJndiName(databaseJndiName);
        dataSource.setCache(true);

        return dataSource;
    }

    @Bean(name="sessionFactory")
    @DependsOn({"dataSource", "flyway"})
    public AnnotationSessionFactoryBean getAnnotationSessionFactoryBean() {
        AnnotationSessionFactoryBean sessionFactory = new AnnotationSessionFactoryBean();

        sessionFactory.setDataSource((DataSource) getDriverManagerDataSource().getObject());
        sessionFactory.setPackagesToScan(new String[] {
            projectPath + ".server.entities",
            projectPath + ".server.entities.joins"
        });

        Properties props = new Properties();
        props.setProperty("hibernate.dialect", hibernateDialect);
        props.setProperty("hibernate.show_sql", hibernateShowSql);
        props.setProperty("hibernate.hbm2ddl.auto", hibernateHbm2dll);
        props.setProperty("hibernate.format_sql", hibernateFormatSql);
        props.setProperty("hibernate.generate_statistics", hibarnateStatistics);
        props.setProperty("hibernate.cache.provider_class", hibarnateCacheProviderClass);
        props.setProperty("hibernate.cache.use_query_cache", hibarnateQueryCache);
        props.setProperty("hibernate.hibernate.cache.provider_configuration_file_resource_path", hibernateEhcacheResource);
        props.setProperty("hibernate.use_second_level_cache", hibarnateSecondLevelCache);
        props.setProperty("hibernate.cache.use_structured_entries", hibernateStructuredEntries);
        props.setProperty("javax.persistence.validation.mode", "none");

        // caching resource
        //props.setProperty("net.sf.ehcache.configurationResourceName", hibernateEhcacheResource);
        //props.setProperty("hibernate.transaction.manager_lookup_class", "nz.co.doltech.ims.server.persistence.TransactionManagerLookup");
        //props.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");

        sessionFactory.setHibernateProperties(props);
        return sessionFactory;
    }

    @Bean(name="transactionManager")
    @DependsOn("sessionFactory")
    public HibernateTransactionManager getHibernateTransactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();

        transactionManager.setSessionFactory(getAnnotationSessionFactoryBean().getObject());

        return transactionManager;
    }

    @Bean(name="hibernateTemplate")
    @DependsOn("sessionFactory")
    public HibernateTemplate getHibernateTemplate() {
        HibernateTemplate hibernateTemplate = new HibernateTemplate();

        hibernateTemplate.setSessionFactory(getAnnotationSessionFactoryBean().getObject());

        return hibernateTemplate;
    }

    @Bean(name="jmxExporter")
    @DependsOn("hibernateStatisticsBean")
    public MBeanExporter getJmxExporter() {
        MBeanExporter exporter = new MBeanExporter();
        Map<String, Object> map = new HashMap<>();

        Properties props = AppProperties.getProperties();
        String name = props.getProperty(AppProperties.CLIENT_MODULE_NAME);
        String type = props.getProperty(AppProperties.CLIENT_RELEASE_STAGE);
        map.put("Hibernate:"+name+"[" + ++statisticId + "]-"+type+"=Statistics", 
            getHibernateStatisticsBean());

        exporter.setBeans(map);
        return exporter;
    }

    @Bean(name="hibernateStatisticsBean")
    public StatisticsService getHibernateStatisticsBean() {
        StatisticsService statsBean = new StatisticsService();

        statsBean.setStatisticsEnabled(true);
        statsBean.setSessionFactory(getAnnotationSessionFactoryBean().getObject());

        return statsBean;
    }

    @Bean(name="incidentDao")
    @DependsOn("hibernateDao")
    public IncidentHibernateDao getIncidentDao() {
        IncidentHibernateDao incidentDao = new IncidentHibernateDao();

        //incidentDao.registerBroadcasterId(AtmosphereConst.UPDATE_ID_KEY);

        return incidentDao;
    }

    @Bean(name="transactionTemplate")
    @DependsOn({"transactionManager"})
    @Scope("prototype")
    public TransactionTemplate getTransactionTemplate() {
        return new TransactionTemplate(getHibernateTransactionManager());
    }
}

这是Hibernate输出:

hibernateDao.get(...) called:
Hibernate: select processent0_.id as id27_0_, processent0_.description as descript2_27_0_, processent0_.name as name27_0_, processent0_.nametoken as nametoken27_0_, processent0_.order_value as order5_27_0_, processent0_.removed as removed27_0_ from processes processent0_ where processent0_.id=?

hibernateDao.fetchLazyCollections(...) called:
Hibernate: select incidentjo0_.process_id as process3_27_1_, incidentjo0_.incident_id as incident2_1_, incidentjo0_.process_id as process3_1_, incidentjo0_.incident_id as incident2_21_0_, incidentjo0_.process_id as process3_21_0_, incidentjo0_.completed as completed21_0_ from incident_process incidentjo0_ where incidentjo0_.process_id=?
Hibernate: select sections0_.process as process27_1_, sections0_.id as id1_, sections0_.id as id29_0_, sections0_.description as descript2_29_0_, sections0_.name as name29_0_, sections0_.order_value as order4_29_0_, sections0_.process as process29_0_, sections0_.removed as removed29_0_ from sections sections0_ where sections0_.process=?

从那时起没有其他任何东西被召唤。

有谁知道这里发生了什么?我没有想法。

感谢我能得到的任何帮助!

干杯, 本

0 个答案:

没有答案