简体   繁体   English

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

[英]LazyLoadInit exception with Spring + Spring JPA data + hibernate

I can't figure this out. 我无法弄清楚这一点。 Am getting "Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.sandy.domain.Location.items, no session or session was closed" I understand that session is closed but tx:annotation-driver @Transactional should ensure an open session. 我得到“线程中的异常”主“org.hibernate.LazyInitializationException:懒得初始化角色集合:org.sandy.domain.Location.items,没有关闭会话或会话”我理解会话已关闭但是tx: annotation-driver @Transactional应确保打开会话。 It works fine with EAGER fetching. 它与EAGER提取工作正常。 Oh and yea - this is by apress pro spring 3 examples. 哦,是的 - 这是通过apress pro spring 3的例子。

But maybe I don't get the concept here - I mean while I am debugging "getFirst()" in SomeService I can see all the items in the collection but once return hits - LazyInit exception is thrown... 但也许我没有得到这个概念 - 我的意思是当我在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;
    }
}

and the service 和服务

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;
    }
}

and location repo 和位置回购

package org.sandy.repo;

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

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

Entities: 实体:

@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;
    }
}

And the all mighty xml bean config: 所有强大的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>

This piece of code 这段代码

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

is equivalent to 相当于

/* 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);

So you can see that the Session boundary is only wrapping the findFirst() call. 因此,您可以看到Session边界仅包含findFirst()调用。 If you're loading your entities lazily, then on line 3, the elements in items are not initialized. 如果您正懒惰地加载实体,那么在第3行, items中的元素不会被初始化。 When you try to call toString() inside List#toString() which calls toString() on each element, you will get your LazyInitializationException . 当您尝试调用toString()List#toString()它调用toString()每个元素,你会得到你LazyInitializationException

You should fully initialize your entities before you use them. 您应该在使用之前完全初始化实体。 That has to be done within Session boundaries. 这必须在Session边界内完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM