我有一個基於Spring的web應用程序,我的問題是在我的代碼更改後,我開始得到延遲加載異常。下面我將介紹詳細情況:奇怪的懶加載異常
一開始
我有一個帳戶和Word實體。一個帳戶可以有多個單詞,一個單詞可以分配給多個帳戶。
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> words;
Word.class
@ManyToMany(targetEntity = Account.class, fetch = FetchType.LAZY, mappedBy = "words")
@JsonIgnore
private List<Account> accounts;
除了每個帳戶可以有其主要原因是Account.class映射字實體代表只有一個 「WordForToday」像這樣:
@OneToOne
@JoinColumn(name="word_for_today")
private Word wordForToday;
一切工作正常。特別是我有一個@Scheduled方法,該方法被調用每天一次改變 「WordForToday」 爲每個帳戶:
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> 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> listUserWords(String username) {
try {
Account foundAccount = accountDao.findUserByUsername(username);
List<Word> 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> c = cb.createQuery(Account.class);
Root<Account> 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> listAccounts() {
CriteriaQuery<Account> cq = cb.createQuery(Account.class);
Root<Account> account = cq.from(Account.class);
cq.select(account);
TypedQuery<Account> q = entityManager.createQuery(cq);
List<Account> accounts = q.getResultList();
return accounts;
}
上面的代碼沒有延遲加載異常。懶惰提取正確的詞。
所以後來
我不得不實施字每個帳戶的團體,所以我在我的項目增加了新的集團實體。現在除了沒有改變的「WordForToday」之外,賬戶和Word之間沒有直接關係。現在一個帳戶可以有多個組,並且只有一個組可以分配給一個帳戶[單向一對多連接表]。
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> 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> words;
Word.class
@ManyToMany(targetEntity = Group.class, fetch = FetchType.LAZY, mappedBy = "words")
@JsonIgnore
private List<Group> groups;
和每一個CRUD方法是否工作正常。我只有問題setNewWordsForToday()高於其現在看起來像這樣提及方法(I重構碼的位):
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> 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> listUserWords(Account account) {
List<Group> userGroups = groupService.listUserGroups(account);
List<Word> userWords = new ArrayList<Word>();
for(Group userGroup : userGroups) {
userWords.addAll(userGroup.getWords());
}
return userWords;
}
}
GroupServiceImpl.class
@Transactional
@Service
public class GroupServiceImpl implements GroupService {
@Override
@Transactional
public List<Group> listUserGroups(Account account) {
List<Group> userGroups = account.getGroups();
userGroups.size();
return userGroups;
}
}
AccountDaoImpl.class沒有更改。現在我已經被調用@Scheduled方法時,這個延遲加載異常:
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 closed
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(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)
我覺得我什麼都試過,我一點也不知道該怎麼辦。它阻止我幾天,現在,請人幫我這個:(
PS:我用的當然OpenEntityManagerInViewFilter
hasListAtLeastOneWordWithDefinitionWhichIsNotSetAsWordForToday:懶讀書例外:) – HRgiger
我知道我知道:P,但它是非常豐富的,我因爲這種方法的邏輯並不明顯 –