延迟初始化异常

Posted

技术标签:

【中文标题】延迟初始化异常【英文标题】:LazyInitializationException 【发布时间】:2012-07-27 14:38:02 【问题描述】:

我得到了这个例外:

javax.el.ELException: Error reading 'id' on type com.example.model.Article_$$_javassist_2
...

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:149)
org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:195)
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
com.example.model.Article_$$_javassist_2.getId(Article_$$_javassist_2.java)
...

这是我的代码:

@Entity
@Table( name = "tbl_articles" )
public class Article implements Comparable<Article>, Serializable

    private static final long serialVersionUID = 1L;

    @Id
    @Column( nullable = false )
    @GeneratedValue( strategy = GenerationType.IDENTITY )
    private Integer id;

    // some other fields

    @ManyToMany( cascade =  CascadeType.ALL  )
    @JoinTable( name = "tbl_articles_categories",
        joinColumns =  @JoinColumn( name = "article_id" ) ,
        inverseJoinColumns =  @JoinColumn( name = "category_id" ) )
    @ForeignKey( name = "tbl_articles_categories_fkey_article",
        inverseName = "tbl_articles_categories_fkey_category" )
    private Set<Category> categories = new HashSet<Category>();

    @ManyToMany( cascade =  CascadeType.ALL  )
    @JoinTable( name = "tbl_articles_tags",
        joinColumns =  @JoinColumn( name = "article_id" ) ,
        inverseJoinColumns =  @JoinColumn( name = "tag_id" ) )
    @ForeignKey( name = "tbl_articles_tags_fkey_article",
        inverseName = "tbl_articles_tags_fkey_tag" )
    private Set<Tag> tags = new HashSet<Tag>();

    // getters and setters


public abstract class BaseService<E, D extends BaseDAO<E>>

    protected D dao;

    public BaseService()
    
    

    protected D getDao()
    
        return dao;
    

    @Autowired
    protected void setDAO( D dao )
    
        this.dao = dao;
    

    @Transactional
    public E get( int id )
    
        return dao.get( id );
    


@Service
public class ArticleService extends BaseService<Article, ArticleDAO>

    public ArticleService()
    
        setDAO( dao );
    


public abstract class BaseDAO<E>

    public abstract E get( int id );


@Repository
public class ArticleDAO extends BaseDAO<Article>

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public Article get( int id )
    
        return ( Article ) sessionFactory.getCurrentSession().load( Article.class, id );
    

现在在我的控制器中,我正在使用它来获取特定的文章:

@RequestMapping( "/id/title.html" )
public String article( @PathVariable( "id" ) Integer id, Map<String, Object> map )

    map.put( "article", articleService.get( id ) );
    return "article";

我在 JSP 中使用的是这样的:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="slg" uri="http://github.com/slugify" %>
<article>
    <c:url value="/blog/$article.id/$slg:slugify(article.title).html" var="articleUrl" />
    <h2><a href="$articleUrl">$article.title</a></h2>
    <span><fmt:formatDate value="$article.creationDate" pattern="E, dd MMM yyyy" /></span>
    <p>
        $article.text
    </p>
</article>

这也是我的休眠配置:

# Properties file with Hibernate Settings.

#-------------------------------------------------------------------------------
# Common Settings

hibernate.generate_statistics=false
#hibernate.hbm2ddl.auto=update
hibernate.show_sql=false

#-------------------------------------------------------------------------------
# DB specific Settings

# Property that determines which Hibernate dialect to use
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

我做错了什么?

更新

调试sessionFactory.getCurrentSession() 导致:

DEBUG : com.example.model.ArticleDAO - SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])

我正在添加一些全局变量,也许这会导致错误?在我的控制器中有一个方法:

@ModelAttribute
public void addGlobalObjects( Map<String, Object> map )

    map.put( "section", "blog" );

    SortedMap<Category, Integer> categories = new TreeMap<Category, Integer>();
    for ( Category category : categoryService.list() )
    
        categories.put( category, articleService.size( category ) );
    

    Calendar cal = Calendar.getInstance();
    cal.set( Calendar.DAY_OF_MONTH, 1 );

    cal.add( Calendar.MONTH, ARCHIVE_MONTHS * -1 );

    SortedMap<Date, Integer> archive = new TreeMap<Date, Integer>();
    for ( int i = 0; i < ARCHIVE_MONTHS; ++i )
    
        cal.add( Calendar.MONTH, 1 );
        archive.put( cal.getTime(), articleService.size( cal ) );
    

    SortedMap<Tag, Integer> tags = new TreeMap<Tag, Integer>();
    for ( Tag tag : tagService.list() )
    
        tags.put( tag, articleService.size( tag ) );
    

    map.put( "categories", categories );
    map.put( "archive", archive );
    map.put( "tags", tags );

    map.put( "categoriesSize", categoryService.size() );
    map.put( "tagsSize", tagService.size() );

    map.put( "date", new Date() );

有关更新的文章实体,请参见上文。

更新2

Eager fetch 没有解决问题 - 仍然是同样的异常:

@ManyToMany( fetch = FetchType.EAGER, cascade =  CascadeType.ALL  )

最后我得到了我不需要的重复文章......

UPDATE3

尝试Hibernate.initialize() 我遇到了这个异常:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session

UPDATE4

我以这种方式更改了我的 get 方法:

@Override
public Article get( int id )

    // return ( Article ) sessionFactory.getCurrentSession().load( Article.class, id );
    return ( Article ) sessionFactory.getCurrentSession().createCriteria( Article.class ).add( Restrictions.eq( "id", id ) ).uniqueResult();

这不是一个解决方案,但由于我无法处理这个问题,我将暂时使用它。

我已经尝试过这个(如提到的here)但没有成功(它将ELException从Error reading 'id' on type ...更改为Error reading 'title' on type ... - 也许我用错了?):

@Override
public Article get( int id )

    Article ret = ( Article ) sessionFactory.getCurrentSession().load( Article.class, id );
    sessionFactory.getCurrentSession().update( ret );
    return ret;

仍然需要解决方案!

【问题讨论】:

【参考方案1】:

这成功了!

将 ArticleDAO.get 更改自

@Override
public Article get( int id )

    return ( Article ) sessionFactory.getCurrentSession().load( Article.class, id );

@Override
public Article get( int id )

    Article ret = ( Article ) sessionFactory.getCurrentSession().get( Article.class, id );
    if ( ret == null )
    
        ret = ( Article ) sessionFactory.getCurrentSession().load( Article.class, id );
    

    return ret;

【讨论】:

【参考方案2】:

由于您使用的是 Hibernate,因此您可以调用 Hibernate.initialize() 来初始化该惰性集合。

另一方面,您可以创建一个 NamedQuery 来获取与您的文章关联的所有项目并替换该集合。但是,仅当您的收藏没有使用 orphanRemoval=true 注释时,此选项才可用。请注意,如果您不小心管理实体,最终可能会在数据库中出现不一致的条目。

【讨论】:

【参考方案3】:

不知道出了什么问题,但有一些问题可以指导您 - 1.能不能调试一下方法

public Article get( int id )

看看你会得到什么

sessionFactory.getCurrentSession()

这是一个有效的会话吗? 2. 你能详细说明一下文章实体吗?与另一个实体有某种关系吗?

我怀疑您的问题介于我的问题之间。 根据您提供的信息更新我的答案 - 正如我所怀疑的,您的 Article 实体与其他实体有关系。 您不执行文章和相关实体的急切获取。 但是,您确实尝试修改与 Article 关联的集合。这是错误的 据我所知,如果您初始化此映射(通过专门分配一个实现 Map 接口的对象并将其设置为字段)(您没有 - 一旦你从数据库中检索到一篇文章,Hibernate 就会为你初始化它,通过阅读 PersistentMap 了解更多信息), 或者您应该修改集合(如果已获取)。 请在此处阅读有关eager 获取的信息。 您还可以编写一个 HSQL 来执行预取,而无需注释。 阅读here 如何使用 HQL 执行提取(搜索左连接提取)。

【讨论】:

以上是关于延迟初始化异常的主要内容,如果未能解决你的问题,请参考以下文章

即使在 HQL 查询中使用 Join Fetch,休眠延迟初始化异常

使用hibernate搜索找不到延迟初始化异常的解决方案

CocosCreator start延迟调用导致帧同步异常

Kotlin的延迟初始化

LazyInitializationException: 延迟初始化角色集合失败 无法初始化代理 - 没有会话

Hibernate org.hibernate.LazyInitializationException:未能延迟初始化角色集合: