2

Spring ベースの webapp を使用していますが、コードを変更した後、遅延読み込みの例外が発生し始めました。以下に状況を詳しく説明します。

はじめに

Account エンティティと Word エンティティがありました。1 つのアカウントに多くの単語を含めることができ、1 つの単語を複数のアカウントに割り当てることができます。

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 にマップされた Word エンティティによって表される「WordForToday」を 1 つだけ持つことができます。

@OneToOne
@JoinColumn(name="word_for_today")
private Word wordForToday;

すべてが正常に機能していました。特に、すべてのアカウントの「WordForToday」を変更するために 1 日に 1 回呼び出される @Scheduled メソッドがありました。

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 の間に直接的な関係はありません。これで、1 つのアカウントが複数のグループを持つことができ、1 つのアカウントに 1 つのグループのみを割り当てることができます [結合テーブルを使用した単方向の 1 対多]。

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;

さらに、1 つのグループに複数の単語を含めることができ、1 つの単語を複数のグループに割り当てることができます。

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()メソッドに問題があるだけで、現在は次のようになっています (コードを少しリファクタリングしました)。

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 を使用しています。

4

2 に答える 2

0

Affeソリューションが機能しない場合、メソッドの実装で異なると思われるのは次のとおりです。

getUserRandomWordWithDefinition(account.getUsername());

エンティティに置き換えられました:

getUserRandomWordWithDefinition(account);

ランダムな単語を呼び出しているときにトランザクションを閉じている可能性はありますか?単語を返すか、アカウントパラメータを送信するためですか?

于 2012-11-27T20:30:03.753 に答える