Transactions in Application scoped component using Quartz timer
fridgebuzz Jan 29, 2009 3:06 PMHi,
I'm having trouble with a no transaction is in progress
exception whenever I try to flush an entity manager in a particular case (I'll explain below.)
There are two separate things going on here that could be the cause and I don't know which. The first is that I'm using an Application-scoped component, and the second is that I'm using an @Asynchronous method (a Quartz scheduled method.) Either one alone could be the problem, so I apologize if I'm confounding the two.
What I have is an Application-scoped component that starts a periodic Quartz-scheduled task to clean up un-needed records in the database.
In shortened form--with some messy details removed--the code looks something like this:
@Name("accountReaper")
@Startup
@Scope(ScopeType.APPLICATION)
public class AccountReaper {
private EntityManagerFactory emf = null;
public AccountReaper() {
emf = Persistence.createEntityManagerFactory("yourview");
reapInterval = reapHours * 60 * 60 * 1000;
reapAccounts(new Date(), reapInterval);
}
@Asynchronous
public QuartzTriggerHandle reapAccounts(@Expiration Date when,
@IntervalDuration Long interval) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, -1*timeoutHours);
Date maxCreationDate = cal.getTime();
pruneUsers(maxCreationDate);
return null;
}
@Transactional
private void pruneUsers(Date maxCreationDate) {
EntityManager entityManager = emf.createEntityManager();
Query query = entityManager.createQuery("select m from User m
where m.creationDate < :date and m.status = :status")
.setParameter("date", maxCreationDate, TemporalType.TIMESTAMP)
.setParameter("status", User.UserStatusType.UNCONFIRMED);
List<User> users = query.getResultList();
for (User user: users) {
entityManager.remove(user);
}
if (!users.isEmpty()) entityManager.flush();
}
The trouble is that the flush() causes a this exception:
javax.persistence.TransactionRequiredException: no transaction is in progress
In a way it makes sense that @Transactional wouldn't work, since that applies to conversations and we're not in conversation-land here anymore (I believe.) Can anyone give me a hint about how to proceed? I don't know if it's the @Asynchronous method that's the problem or the Application-scope of the component that's the problem.
The only solution I can think of to try is to create my own transaction, with something like (leaving out the try/catch stuff for brevity):
UserTransaction transaction = Transaction.instance();
transaction.begin();
for (User user: users) {
entityManager.remove(user);
}
if (!users.isEmpty()) entityManager.flush();
transaction.commit();
Am I on the right track? Can anyone suggest a better solution or explain the cause of the problem?
Thank you all for your attention,
Vanessa