Title: Strange lazy loading exception
Tags: java;spring;jpa
Question: I have a Spring-based webapp and my problem is after a change in my code I started to get lazy loading exception. Below I describe the situation in details:
In the beginning
I had an Account and Word entities. One account can have many words and one Word can be assigned to many Accounts.
Account.class
@ManyToMany(targetEntity = Word.class, fetch = FetchType.LAZY)
@JoinTable(name = "account_word", joinColumns = {@JoinColumn(name="account_id")}, inverseJoinColumns = {@JoinColumn(name="word_id")})
@OrderBy("word")
private List<Word&gt; words;
Word.class
@ManyToMany(targetEntity = Account.class, fetch = FetchType.LAZY, mappedBy = "words")
@JsonIgnore
private List<Account&gt; accounts;
Except that every Account can have only one "WordForToday" which was represented by Word entity mapped in Account.class like this:
@OneToOne
@JoinColumn(name="word_for_today")
private Word wordForToday;
Everything was working properly. In particular I had a @Scheduled method which was invoked once a day to change the "WordForToday" for every Account:
WordServiceImpl.class
@Transactional
@Service
public class WordServiceImpl implements WordService {
@Autowired
AccountDao accountDao;
@PersistenceContext
EntityManager entityManager;
@Override
@Scheduled(cron="0 0 0 * * ?")
public void setNewWordsForToday() {
logger.info("Starting setting new Words For Today");
List<Account&gt; allAccounts = accountDao.listAccounts();
for(Account account : allAccounts) {
if(hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday(account.getWords(), account.getUsername())) {
account.setWordForToday(getUserRandomWordWithDefinition(account.getUsername()));
entityManager.persist(account);
}
logger.info("Setting new Words For Today ended");
@Override
@Transactional
public List<Word&gt; listUserWords(String username) {
try {
Account foundAccount = accountDao.findUserByUsername(username);
List<Word&gt; userWords = foundAccount.getWords();
userWords.size();
return userWords;
} catch (UserNotFoundException unf) {
logger.error("User not found: " + username, unf.getMessage());
return Collections.emptyList();
AccountDaoImpl.class
@Override
public Account findUserByUsername(String username) throws UserNotFoundException {
CriteriaQuery<Account&gt; c = cb.createQuery(Account.class);
Root<Account&gt; r = c.from(Account.class);
try {
c.select(r).where(cb.equal(r.get("username"), username));
Account foundAccount = entityManager.createQuery(c).getSingleResult();
return foundAccount;
} catch(NoResultException nre){
throw new UserNotFoundException();
@Override
public List<Account&gt; listAccounts() {
CriteriaQuery<Account&gt; cq = cb.createQuery(Account.class);
Root<Account&gt; account = cq.from(Account.class);
cq.select(account);
TypedQuery<Account&gt; q = entityManager.createQuery(cq);
List<Account&gt; accounts = q.getResultList();
return accounts;
And this code above was without lazy loading exception. Words where lazy fetched properly.
So then
I had to implement Groups of Words for every Account, so I added new Group entity in my project. Now there is no direct relationship between Account and Word except "WordForToday" which didn't changed. Now one Account can have many Groups and only one Group can be assigned to one Account [unidirectional one-to-many with join table].
Account.class
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name = "account_wordgroup", joinColumns = {@JoinColumn(name="account_id")}, inverseJoinColumns = {@JoinColumn(name="wordgroup_id")})
@OrderBy("name")
private List<Group&gt; groups;
Additionally one Group can have many words and one Word can be assigned to many groups.
Group.class
@ManyToMany(targetEntity = Word.class, fetch = FetchType.EAGER)
@OrderBy(value="word")
@JoinTable(name = "wordgroup_word", joinColumns = {@JoinColumn(name="wordgroup_id")}, inverseJoinColumns = {@JoinColumn(name="word_id")})
private List<Word&gt; words;
Word.class
@ManyToMany(targetEntity = Group.class, fetch = FetchType.LAZY, mappedBy = "words")
@JsonIgnore
private List<Group&gt; groups;
And every CRUD methods which use this Entities above are working properly. I only have problem with setNewWordsForToday() method mentioned above which now looks like this (I refactored code a bit):
WordServiceImpl.class
@Transactional
@Service
public class WordServiceImpl implements WordService {
@Autowired
AccountDao accountDao;
@PersistenceContext
EntityManager entityManager;
@Autowired
GroupService groupService;
@Override
@Scheduled(cron="0 0 0 * * ?")
@Transactional
public void setNewWordsForToday() {
logger.info("Starting setting new Words For Today");
List<Account&gt; allAccounts = accountDao.listAccounts();
for(Account account : allAccounts) {
if(hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday(listUserWords(account), account)) {
account.setWordForToday(getUserRandomWordWithDefinition(account));
entityManager.persist(account);
}
logger.info("Setting new Words For Today ended");
@Override
@Transactional
public List<Word&gt; listUserWords(Account account) {
List<Group&gt; userGroups = groupService.listUserGroups(account);
List<Word&gt; userWords = new ArrayList<Word&gt;();
for(Group userGroup : userGroups) {
userWords.addAll(userGroup.getWords());
return userWords;
GroupServiceImpl.class
@Transactional
@Service
public class GroupServiceImpl implements GroupService {
@Override
@Transactional
public List<Group&gt; listUserGroups(Account account) {
List<Group&gt; userGroups = account.getGroups();
userGroups.size();
return userGroups;
AccountDaoImpl.class didn't change. And Now i have this lazy loading exception when @Scheduled method is invoked:
ERROR [org.springframework.scheduling.support.MethodInvokingRunnable] - Invocation of method 'setNewWordsForToday' on target class [class pl.net.grodek.snd.service.WordServiceImpl] failed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: pl.net.grodek.snd.model.Account.groups, no session or session was gonzalezcory@example.net(AbstractPersistentCollection.java:394)
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:386)
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:126)
at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:242)
at pl.net.grodek.snd.service.GroupServiceImpl.listUserGroups(GroupServiceImpl.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy48.listUserGroups(Unknown Source)
at pl.net.grodek.snd.service.WordServiceImpl.listUserWords(WordServiceImpl.java:83)
at pl.net.grodek.snd.service.WordServiceImpl.setNewWordsForToday(WordServiceImpl.java:333)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:273)
at org.springframework.scheduling.support.MethodInvokingRunnable.run(MethodInvokingRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:51)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:206)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
I think that i tried everything and I have no more idea what to do. It is blocking me for a couple of days for now so please anyone help me with this :(
PS: I am using OpenEntityManagerInViewFilter of course.
Comment: I know i know :P but it is very informative for me because logic of this method is not obvious

Comment: hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday : lazy reading exception:)

Here is the accepted answer: The task scheduler is executing that method directly on the service implementation, not going through the Proxy, so it is not picking up the @Transactional and starting a Hibernate Session at that scope.
Try creating another bean that wires in WordService and put the schedule there, so that you invoke the method on the WordService proxy.  That should clear it up.
Comment for this answer: To clarify I have to create a bean and put there @Scheduled method and then only Autowired it into WordService bean?
Comment for this answer: @Affe, you are a genius. This fixed the problem for me too... Nice to know scheduled jobs must cross a service boundary to start that transaction at start of execution.
Comment for this answer: No, AutoWire the WordService bean into your new bean and call wordService.setNewWordsForToday(); from inside scheduled method in your new bean.
Comment for this answer: Happy to help :) some version of that problem bites everyone who starts using Spring with JDK-Proxies sooner or later.

Here is another answer: If Affe solution doesnt work, what I see different in your method implementation is :
getUserRandomWordWithDefinition(account.getUsername());
replaced by entity:
getUserRandomWordWithDefinition(account);
Is that can be possible you are closing your transaction while you are calling random word? Because of returning word or sending account parameter?
Comment for this answer: thanks for your help but i tried it before i wrote this question

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 2, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2012:00:11/27/12', 'FavoriteCount': 1.0, 'PostTypeId': 1, 'Tags': '<java><spring><jpa>', 'Title': 'Strange lazy loading exception', 'ViewCount': 1408, 'id': 13592223, 'user_AboutMe': '<p>Im working in Cracow as a Java Developer. </p>\n', 'user_AccountId': 1193704.0, 'user_DisplayName': 'Mariusz Grodek', 'user_Reputation': 609.0, 'user_Views': 159.0, 'user_id': 1165899.0}}