jpa #postpersist #postupdate only after transaction commit - jpa

I'm inserting/updating 3 tables while using manual transaction. I want to insert into an history table right after the transaction was committed. that means that there were 3 em.persist actions (for each table), and only after committing them I would like to call a method.
This is my code:
#Resource
private EJBContext context;
public void save(Object obj)
{
try
{
transaction = context.getUserTransaction();
transaction.begin();
em.persist(obj);
sb2.save(obj); //persist in sb2
sb3.save(obj); //persist in sb2
transaction.commit();
}
catch (Exception exp)
{
transaction.rollback();
}
}
Is there any kind of post commit method?
How can I call a method after the commit, and not after the persist?

You could use TransactionSynchronizationRegistry to register a callback to be executed after the transaction completes:
public class MyListener {
// if you can't inject it with #Resource, you'll have to fetch it from JNDI
#Resource
private javax.transaction.TransactionSynchronizationRegistry registry;
#PostUpdate
public void postUpdate(final Object entity) {
registry.registerInterposedSynchronization(new Synchronization() {
public void beforeCompletion() {}
public void afterCompletion(int status) {
if (status == javax.transaction.Status.STATUS_COMMITTED) {
// Do something with your entity
}
}
});
}
}
Then annotate your entities with #EntityListeners(MyListener.class) (or add it in your orm.xml).

If you are in a JPA 2.2 + CDI + JTA environment, you could leverage CDI events which can be observed at a specified transaction phase.
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface EntityEvent {}
public class MyListener {
#Inject
#EntityEvent
private Event<Object> event;
#PostUpdate
public void postUpdate(Object entity) {
event.fire(entity);
}
}
public class MyObserver {
public void afterCommit(#Observes(during = TransactionPhase.AFTER_SUCCESS) #EntityEvent Object entity) {
// do something...
}
}
#EntityListeners(MyListener.class)
public class MyEntity {}

JPA does not provide such events, but EclipseLink provides extended events through the SessionEventListener API.
http://eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_session_event_listener.htm
You could also consider the container JTA events.

Related

Transaction is required to perform this operation (either use a transaction or extended persistence context) while persisting Entity

I am trying to develop a class that runs at specific intervals and performs some DB modifications.
the code I have managed to run at a specific interval, retrieve records from the DB, but when I want to commit changes to the DB I get the following error.
WFLYEE0110: Failed to run scheduled task: javax.persistence.TransactionRequiredException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)
is #ApplicationScoped allowed to create transactions?
Thanks!
#ApplicationScoped
#ActivateRequestContext
public class TaskRunner {
#PersistenceContext(type = PersistenceContextType.EXTENDED)
EntityManager em;
#Resource private ManagedScheduledExecutorService scheduler;
private ScheduledFuture<?> TaskRunnerScheduler;
private boolean initialized = false;
private void init(#Observes #Initialized(ApplicationScoped.class) Object init) {
if (initialized) return;
initialized = true;
try {
// Execute at startup
TaskRunner = scheduler.schedule(this::runSchedule, getSchedule());
} catch (Throwable throwable) {
}
}
#Transactional
private void runSchedule() {
//retrieve db records
//make changes and commit
//sample
//em.persist(someEntity)
}
private Trigger getSchedule() {
return new Trigger() {
#Override
public Date getNextRunTime(LastExecution lastExecutionInfo, Date taskScheduledTime) {
return Date.from(
ZonedDateTime.now().withSecond(0).withNano(0).plusHours("4").toInstant());
}
#Override
public boolean skipRun(LastExecution lastExecutionInfo, Date scheduledRunTime)
{return false;}};
}
}
Transactions are started via Interceptors.
When you call a method of a bean from inside that bean, the method-call is not intercepted and no Transaction can get started.
You need another Bean and persist in there
#RequestScoped
#Transactional(value = TxType.REQUIRES_NEW)
public class SomeOtherBean{
#PersistenceContext(type = PersistenceContextType.EXTENDED)
EntityManager em;
public void doSomething(){
//retrieve db records
//make changes and commit
//sample
//em.persist(someEntity)
}
}
then you can Inject that bean in your TaskRunner
#ApplicationScoped
#ActivateRequestContext
public class TaskRunner {
#Inject
SomeOtherBean someBean;
...
private void runSchedule() {
someBean.doSomething()
}
}

Spring Transactions - How to access to entity saved in parent transaction

consider following model:
#Service
public class TripServiceImpl implements TripService {
#Autowired
private EventService eventService;
#Autowired
private CalendarService calendarService;
#Transactional
public void processTrip(TripDto dto) {
EventDto event = eventService.findByTripId(dto).orElseGet(() -> eventService.createByTrip(dto));
dto.getMembers().forEach(memberCode -> {
try {
calendarService.createReminder(event.getId(), memberCode);
} catch (Exception ex) {}
});
// rest logic
}
}
This is how createReminder method looks like:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void createReminder(Long eventId, String memberCode) {
Optiona<Event> eventRepository.findById(eventId); // this returns Optional.empty()
...
}
In our trip service we find or create event for given trip if no exists, and add reminders to calendars of members from that trip. Method for creating reminders we need as new transaction to ensure, if that method failed with exception, don't rollback all changes. This transaction propagation unfortunately can't find entity which was saved in parent transactional method. Can you tell me how to fix it? Thank you.

Spring Boot Hibernate Postgresql #Transactional does not rollback [duplicate]

I want to read text data fixtures (CSV files) at the start on my application and put it in my database.
For that, I have created a PopulationService with an initialization method (#PostConstruct annotation).
I also want them to be executed in a single transaction, and hence I added #Transactional on the same method.
However, the #Transactional seems to be ignored :
The transaction is started / stopped at my low level DAO methods.
Do I need to manage the transaction manually then ?
Quote from legacy (closed) Spring forum:
In the #PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.
So if you would like something in your #PostConstruct to be executed within transaction you have to do something like this:
#Service("something")
public class Something {
#Autowired
#Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
#PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
I think #PostConstruct only ensures the preprocessing/injection of your current class is finished. It does not mean that the initialization of the whole application context is finished.
However you can use the spring event system to receive an event when the initialization of the application context is finished:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
See the documentation section Standard and Custom Events for more details.
As an update, from Spring 4.2 the #EventListener annotation allows a cleaner implementation:
#Service
public class InitService {
#Autowired
MyDAO myDAO;
#EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
event.getApplicationContext().getBean(InitService.class).initialize();
}
#Transactional
public void initialize() {
// use the DAO
}
}
Inject self and call through it the #Transactional method
public class AccountService {
#Autowired
private AccountService self;
#Transactional
public void resetAllAccounts(){
//...
}
#PostConstruct
private void init(){
self.resetAllAccounts();
}
}
For older Spring versions which do not support self-injection, inject BeanFactory and get self as beanFactory.getBean(AccountService.class)
EDIT
It looks like that since this solution has been posted 1.5 years ago developers are still under impression that if a method,
annotated with #Transactional, is called from a #PostContruct-annotated method invoked upon the Bean initialization, it won't be actually executed inside of Spring Transaction, and awkward (obsolete?) solutions get discussed and accepted instead of this very simple and straightforward one and the latter even gets downvoted.
The Doubting Thomases :) are welcome to check out an example Spring Boot application at GitHub which implements the described above solution.
What actually causes, IMHO, the confusion: the call to #Transactional method should be done through a proxied version of a Bean where such method is defined.
When a #Transactional method is called from another Bean, that another Bean usually injects this one and invokes its proxied (e.g. through #Autowired) version of it, and everything is fine.
When a #Transactional method is called from the same Bean directly, through usual Java call, the Spring AOP/Proxy machinery is not involved and the method is not executed inside of Transaction.
When, as in the suggested solution, a #Transactional method is called from the same Bean through self-injected proxy (self field), the situation is basically equivalent to a case 1.
#Platon Serbin's answer didn't work for me. So I kept searching and found the following answer that saved my life. :D
The answer is here No Session Hibernate in #PostConstruct, which I took the liberty to transcribe:
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
The transaction part of spring might not be initialized completely at #PostConstruct.
Use a listener to the ContextRefreshedEvent event to ensure, that transactions are available:
#Component
public class YourService
implements ApplicationListener<ContextRefreshedEvent> // <= ensure correct timing!
{
private final YourRepo repo;
public YourService (YourRepo repo) {this.repo = repo;}
#Transactional // <= ensure transaction!
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repo.doSomethingWithinTransaction();
}
}
Using transactionOperations.execute() in #PostConstruct or in #NoTransaction method both works
#Service
public class ConfigurationService implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
private ConfigDAO dao;
private TransactionOperations transactionOperations;
#Autowired
public void setTransactionOperations(TransactionOperations transactionOperations) {
this.transactionOperations = transactionOperations;
}
#Autowired
public void setConfigurationDAO(ConfigDAO dao) {
this.dao = dao;
}
#PostConstruct
public void postConstruct() {
try { transactionOperations.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(final TransactionStatus status) {
ResultSet<Config> configs = dao.queryAll();
}
});
}
catch (Exception ex)
{
LOG.trace(ex.getMessage(), ex);
}
}
#NoTransaction
public void saveConfiguration(final Configuration configuration, final boolean applicationSpecific) {
String name = configuration.getName();
Configuration original = transactionOperations.execute((TransactionCallback<Configuration>) status ->
getConfiguration(configuration.getName(), applicationSpecific, null));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
}

How to change my DAO Bean from EJB to pure CDI?

I want to reuse my AbstractDAO in a new project, except this time I don't want to use EJB annotations - just CDI ones.
So far, I've been using it like this:
public abstract class AbstractDAO<T> {
#PersistenceContext(unitName = "myUnit")
private EntityManager entityManager;
private Class<T> entityClass;
public AbstractDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected EntityManager getEntityManager() {
return entityManager;
}
public void save(T entity) {
entityManager.persist(entity);
}
public void update(T entity) {
entityManager.merge(entity);
}
public void remove(T entity) {
entityManager.remove(entityManager.merge(entity));
}
public T findById(Object id) {
return entityManager.find(entityClass, id);
}
public List<T> findBy(String attrName, Object attrValue) {
// Impl here
}
// [...] Many more search methods
}
And I've been creating a DAO for each Entity, like this for example:
#Stateless
public class UserDAO extends AbstractDAO<User> {
public UserDAO() {
super(User.class);
}
public User findByUsername(String username) {
if (username != null) {
return super.findOneBy("username", username.toLowerCase());
}
return null;
}
}
Now I would like to get rid of the #Stateless annotation. But simply replacing it with a #RequestScoped one won't work because of the non-private constructor with no parameters requirement of JSR-346
How can I refactor my DAO to a pure CDI one ?
Two issues here: CDI beans are not transaction aware by default -unlike EJBs, so you will have to use #Transactional qualifier if you want to do saves/updates...
Second, your no-arg constructor: you only need to pass the entity class to your abstract class even though you also specify it as a generic argument. You can infer the actual class like this:
public class AbstractDAO<T> {
private transient Class<T> entityClass;
#SuppressWarnings("unchecked")
public AbstractDAO() {
Type generSuperCls = getClass().getGenericSuperclass();
if (generSuperCls instanceof Class) {
generSuperCls = ((Class<?>) generSuperCls).getGenericSuperclass();
}
ParameterizedType parameterizedType = (ParameterizedType) generSuperCls;
Type type = parameterizedType.getActualTypeArguments()[0];
if (type instanceof Class) {
this.entityClass = (Class<T>) type;
} else if (type instanceof ParameterizedType) {
this.entityClass = (Class<T>) ((ParameterizedType) type).getRawType();
}
}
#PersistenceContext
private EntityManager em;
public T getById(Object id) throws ServiceException {
return getEm().find(entityClass, id);
}
// other methods follow
}
As a side note, why do you want to get rid of EJBs? Benchmarks show you get better performance using pooled slsb than cdi and they together very well(every EJB beans is also a CDI bean in jee container).

Is there any reason to use ObjectContext transaction handling with DbContext's SaveChanges?

I have a code like this:
public abstract class DataContextBase
{
public DbContext DbContext { get; protected internal set; }
public ObjectContext ObjectContext { get; protected internal set; }
protected DbTransaction transaction;
protected void SetContext(DbContext db, ObjectContext oc)
{
DbContext = db;
ObjectContext = oc;
}
public void BeginTransaction()
{
if (ObjectContext.Connection.State != System.Data.ConnectionState.Open)
{
ObjectContext.Connection.Open();
}
transaction = ObjectContext.Connection.BeginTransaction();
}
public void CommitTransaction()
{
try
{
transaction.Commit();
}
finally
{
transaction = null;
ObjectContext.Connection.Close();
}
}
public void RollbackTransaction()
{
try
{
transaction.Rollback();
}
finally
{
transaction = null;
ObjectContext.Connection.Close();
}
}
public void Save()
{
DbContext.SaveChanges();
}
}
It is from a sample application, and I use this as a base class of my application's main data context. I'm using Entity Framework 5, and I have just read that when I call the DbContext's SaveChanges method, it always runs in a database transaction and it will throw an exception when the transaction have to be rollbacked and in this case the changes are not saved into the database.
But in the sample application, almost every service method begins with a DataContextBase.BeginTransaction call and ends with a DataContextBase.CommitTransaction call (in an exceptional case it ends with DataContextBase.RollbackTransaction) even though that DataContextBase.Save is called (which calls DbContext.SaveChanges()).
It looks like there is an extra transaction wrapping the built in transaction of the DbContext.SaveChanges call.
Could there be any situation which needs this extra transaction?
NOTE: The DataContextBase's ObjectContext is come from the DbContext with a trick:
((IObjectContextAdapter)this).ObjectContext; // inside the DbContext class
Having an extra transaction is redundant because ObjectContext/DbContext implements Unit of Work. If you have other means of communicating with the database and they also need to be part of the transaction the use TransactionScope.
Connection management is also done by EF and you do not have to