使用Spring + Spring JPA数据+ hibernate的LazyLoadInit异常

时间:2013-11-18 18:55:48

标签: spring jpa lazy-loading spring-data-jpa

我无法弄清楚这一点。得到“线程中的异常”主“org.hibernate.LazyInitializationException:懒得初始化角色集合:org.sandy.domain.Location.items,没有关闭会话或会话” 我知道会话已关闭,但tx:annotation-driver @Transactional应确保打开会话。它与EAGER提取工作正常。哦,是的 - 这是通过apress pro spring 3的例子。

但也许我没有得到这个概念 - 我的意思是当我在SomeService中调试“getFirst()”时我可以看到集合中的所有项目但是一旦返回命中 - 抛出LazyInit异常......

package org.sandy.main;

    import org.sandy.domain.Item;
    import org.sandy.domain.Location;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.support.GenericXmlApplicationContext;
    import org.springframework.stereotype.Service;

@Service(value = "main")
public class EntryPoint {

    @Autowired
    private SomeService ss;

    public static void main(String args[]) {
        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
        ctx.load("classpath:*-beans.xml");
        ctx.refresh();

        EntryPoint entryPoint = (EntryPoint) ctx.getBean("main");

        Item item = new Item();
        Location location = new Location();

        item.setLocation(location);
        location.getItems().add(item);

        entryPoint.getSs().save(location);

        System.out.println(entryPoint.getSs().findFirst());

        ctx.registerShutdownHook();
    }

    public SomeService getSs() {
        return ss;
    }

    public void setSs(SomeService ss) {
        this.ss = ss;
    }
}

和服务

package org.sandy.main;

import org.sandy.domain.Item;
import org.sandy.domain.Location;
import org.sandy.repo.ItemRepo;
import org.sandy.repo.LocationRepo;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service(value = "ss")
@Repository
@Transactional
public class SomeService {
    @Autowired
    private ItemRepo itemRepo;
    @Autowired
    private LocationRepo locationRepo;

    @Transactional
    public void save(Location location) {
        locationRepo.save(location);
    }

    @Transactional(readOnly = true)
    public List<Item> findFirst() {
        System.out.println("ONE");
        Iterable<Location> it = locationRepo.findAll();
        System.out.println("TWO");
        Location l = it.iterator().next();
        System.out.println("THREE");
        List<Item> r = l.getItems();
        return r;
    }

    @Transactional
    public Iterable finAll() {
        return locationRepo.findAll();
    }

    public void setItemRepo(ItemRepo itemRepo) {
        this.itemRepo = itemRepo;
    }

    public void setLocationRepo(LocationRepo locationRepo) {
        this.locationRepo = locationRepo;
    }
}

和位置回购

package org.sandy.repo;

import org.sandy.domain.Location;
import org.springframework.data.repository.CrudRepository;

public interface LocationRepo extends CrudRepository<Location, Long> {
}

实体:

@Entity
@Table(name = "Locations")
public class Location extends Entry {

    private List<Item> items = new ArrayList<Item>();

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "location")
    public List<Item> getItems() {
        return items;
    }

    public void setItems(List<Item> items) {
        this.items = items;
    }
}

package org.sandy.domain;

import javax.persistence.*;

@Entity
@Table(name = "Items")
public class Item extends Entry {
    private Location location;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn()
    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }
}

所有强大的xml bean配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <jdbc:embedded-database id="dataSource" type="H2" />

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emf"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="packagesToScan" value="org.sandy.domain" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.H2Dialect
                </prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
            </props>
        </property>
    </bean>

    <context:annotation-config/>

    <jpa:repositories base-package="org.sandy.repo" entity-manager-factory-ref="emf" transaction-manager-ref="transactionManager" />

    <context:component-scan base-package="org.sandy" />
</beans>

1 个答案:

答案 0 :(得分:2)

这段代码

System.out.println(entryPoint.getSs().findFirst());

相当于

/* 1 */ SomeService ss = entryPoint.getSs();
/* 2 */ List<Item> items = ss.findFirst(); // Session opens and closes for @Transactional 
/* 3 */ String toPrint = items.toString(); // no more Session
/* 4 */ System.out.println(toPrint);

因此,您可以看到Session边界仅包含findFirst()调用。如果您正懒惰地加载实体,那么在第3行,items中的元素不会被初始化。当您尝试在每个元素上调用toString()的{​​{1}}内拨打List#toString()时,您将获得toString()

在使用实体之前,您应该完全初始化实体。这必须在Session边界内完成。