在此队列处理中,我希望将发送本身放入单独的事务中,以避免回滚已发送的状态。但是,由于在延迟提取时关闭的会话或分离的实体,我遇到了问题。
这是我的代码:
// Before this is called queueToSend is fetched in a separate transaction
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void sendMails(List<QueuedMail> queueToSend)
{
for (QueuedMail queuedMail: queueToSend) {
sendMail(queuedMail);
}
}
public void sendMail(QueuedMail queuedMail)
{
System.out.println("Checking "+queuedMail);
crud.getEntityManager().merge(queuedMail);
Mail mail = (Mail)crud.find(queuedMail.getMail().getId(),Mail.class);
System.out.println("mail id: "+queuedMail.getMail().getId());
crud.getEntityManager().merge(mail);
Boolean unsubscribed = false;
Set<Campaign> campaigns = mail.getCampaigns();
for (Campaign campaign: campaigns) {
if (campaign.getUnsubscribedUsers().contains(queuedMail.getUser())) {
unsubscribed = true;
}
}
// ...
}
错误是:
failed to lazily initialize a collection of role: dps.simplemailing.entities.Mail.campaigns, could not initialize proxy - no Session
排在第一位:
for (Campaign campaign: campaigns) {
我认为可能是因为queuedMail已经分离,但是我尝试使用merge重新连接queuedMail和mail,但它没有帮助。
也许它已经在缓存中,这就是为什么它不会启动新会话。
基本上我希望它像以前一样(在我添加TransactionAttribute之前),但作为循环中的单独事务。我不认为我应该做任何特定于供应商的解决方案,因为它似乎是一项微不足道的任务。
更新
我做了一些研究,发现我必须使用合并操作的结果才能重新附加分离的实体。这将特定于供应商的错误(因为使用未定义的分离实体的延迟加载属性)更改为与供应商无关的错误,我认为它显示了真正的问题。
修改后的代码是:
public void sendMail(QueuedMail queuedMail)
{
System.out.println("Checking "+queuedMail);
queuedMail = crud.getEntityManager().merge(queuedMail);
Mail mail = (Mail)crud.getEntityManager().merge(queuedMail.getMail());
Boolean unsubscribed = false;
Set<Campaign> campaigns = mail.getCampaigns();
for (Campaign campaign: campaigns) {
if (campaign.getUnsubscribedUsers().contains(queuedMail.getUser())) {
unsubscribed = true;
}
}
我还更改了实体以定义级联类型:
@ManyToMany(mappedBy = "mails",cascade = CascadeType.MERGE)
private Set<Campaign> campaigns;
现在错误是这样的:
Transaction is required to perform this operation (either use a transaction or extended persistence context)
我对此错误并不十分理解,该事务应该已经启动,因为类默认为TransactionAttributeType.REQUIRED
。将其他方法设置为TransactionAttributeType.NOT_SUPPORTED
的意图是在此方法中启动事务。我是交易和实体经理的初学者,所以我会继续研究以更好地理解它,但也许我会尽快得到答案,或者如果我找到解决方案,我会发布它。
答案 0 :(得分:0)
我发现了一个小故障,这很难说,没有进一步的研究表明真正的问题,但我在抽出时间和走路时得到了答案。
正如我所说,这种方法的主要思想是sendMails
没有交易,而sendMail
(应该)有交易,这必须为每个项目提供一个交易待处理。
更新中的错误向我显示sendMail
中没有任何交易,我对此感到很困惑。
真正的原因在于使用代理执行bean的事实。通常这是从bean本身外部完成的。如果bean直接调用它自己的方法,则容器没有机会代理请求,也不能启动事务。
更正后的代码在bean中有一个注入点,用于注入它自己的实例,但使用容器:
@Stateless
public class MailQueue {
@Inject MailQueue mailQueue;
// ...
}
使用此注入点,一个方法可以通过代理调用另一个方法:
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void sendMails(List<QueuedMail> queueToSend)
{
for (QueuedMail queuedMail: queueToSend) {
mailQueue.sendMail(queuedMail);
}
}
public void sendMail(QueuedMail queuedMail)
{
// ...
}
使用此解决方案,容器有机会在调用sendMail之前启动新事务。