Reputation: 4805
I'm getting this exceptions:
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)
...
Here's my code:
@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 );
}
}
In my Controller now, I'm using this to get a specific article:
@RequestMapping( "/{id}/{title}.html" )
public String article( @PathVariable( "id" ) Integer id, Map<String, Object> map )
{
map.put( "article", articleService.get( id ) );
return "article";
}
Which I'm using in my JSP just like this:
<%@ 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>
Here's my hibernate configuration as well:
# 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
What I'm doing wrong?
UPDATE
Debugging sessionFactory.getCurrentSession()
results in:
DEBUG : com.example.model.ArticleDAO - SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])
I'm adding some global variables, maybe this causing the errors? In my controller there's a method:
@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() );
}
For updated article entity see above.
UPDATE2
Eager fetching didn't solved the problem - still the same exception:
@ManyToMany( fetch = FetchType.EAGER, cascade = { CascadeType.ALL } )
And finally I'm getting duplicated articles what I don't need...
UPDATE3
Trying Hibernate.initialize()
I'm getting this exception:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
UPDATE4
I changed my get method this way:
@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();
}
This is NOT a solution, but as I can't handle this problem I'll use this temporarily.
I already tried this (as mentioned here) without success (it changed the ELException from Error reading 'id' on type ...
to Error reading 'title' on type ...
- maybe i used it wrong?):
@Override
public Article get( int id )
{
Article ret = ( Article ) sessionFactory.getCurrentSession().load( Article.class, id );
sessionFactory.getCurrentSession().update( ret );
return ret;
}
A solution is still needed!
Upvotes: 2
Views: 2762
Reputation: 4805
This did the trick!
Changed ArticleDAO.get from
@Override
public Article get( int id )
{
return ( Article ) sessionFactory.getCurrentSession().load( Article.class, id );
}
to
@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;
}
Upvotes: 2
Reputation: 10055
Since you're using Hibernate you can invoke Hibernate.initialize()
to initialize that lazy collection.
On the other hand, you can create a NamedQuery that fetches all the items that are associated to your Article and replacing that collection. However, this option is only possible if you don't have your collection annotated with orphanRemoval=true
. Watch out, since you can end up having inconsistent entries in your database if you don't manage your entities with care.
Upvotes: 1
Reputation: 4137
Not sure what's wrong but some questions to guide you -
1. Can you debug the method
public Article get( int id )
And see what do you get for
sessionFactory.getCurrentSession()
Is this a valid session?
2. Can you elaborate more on the Article entity? is there some sort of relation to another entity?
I suspect your issues lies somewhere between my questions.
Update to my answer, following the information your provided -
As I suspected, your Article entity has relation with other entities.
You do not perform eager fetching of Article and the related entities.
However, you do try to modify the collection associated with Article. This is wrong
As far as I know, you should can modify the collection (the map in your case) either if you initialized this map (by specifically allocating an object that impelements the Map interface, and set it to the field)
(which you didn't - Hibernate initialized it for you once you retrieved an article from the DB, see more by reading about PersistentMap),
or you should modify the collection if it was fetched.
Please read here about eager fetching.
You can also write an HSQL which will perform the eager fetch, without an annotation.
Read here how to perform fetching using HQL (search for left join fetch).
Upvotes: 1