grails-data-mapping
grails-data-mapping copied to clipboard
@Transactional not applied to setter-like service method
Task List
- [x] Steps to reproduce provided
- [x] Stacktrace (if present) provided
- [ ] Example that reproduces the problem uploaded to Github
- [x] Full description of the issue provided (see below)
Steps to Reproduce
- Create a grails service with
@Transactional
annotation - Create a method on that service called
setSystemConfiguration(List<...> configuration)
which should iterate the list an persist new instances ofSystemConfiguration
. No actual property like that exists on the service. Well it's a service, it does stuff, not hold stuff! - invoke that method.
Expected Behaviour
SystemConfiguration
objects are persisted to DB.
Actual Behaviour
setSystemConfiguration()
does seemingly nothing (when build with Hibernate 5.1.x) or throws an exception (with Hibernate 5.2.x) shown below.
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3505)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1427)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1423)
at org.grails.orm.hibernate.AbstractHibernateGormInstanceApi.flushSession(AbstractHibernateGormInstanceApi.groovy:289)
at org.grails.orm.hibernate.AbstractHibernateGormInstanceApi$_performSave_closure3.doCall(AbstractHibernateGormInstanceApi.groovy:254)
at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:299)
at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:243)
at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:117)
at org.grails.orm.hibernate.AbstractHibernateGormInstanceApi.performSave(AbstractHibernateGormInstanceApi.groovy:251)
at org.grails.orm.hibernate.AbstractHibernateGormInstanceApi.save(AbstractHibernateGormInstanceApi.groovy:168)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.save(GormEntity.groovy:151)
at xxx.xx.xxxx.ConfigurationService$_setSystemConfiguration_closure51$$ERBJdcoH.doCall(ConfigurationService.groovy:1005)
at xxx.xx.xxxx.ConfigurationService$$ERBJdcnT.setSystemConfiguration(ConfigurationService.groovy:1002)
at xxx.xx.xxxx.AdminController.setSystemConfiguration(AdminController.groovy:305)
... 42 common frames omitted
Environment Information
- Operating System: WinX
- GORM Version: 6.1.11
- Grails Version (if using Grails): 3.3.9
- JDK Version: 1.8.0_192
Description
From what I've gathered it's how class-level @Transactional
transform is written - setters are skipped from adding transactional behaviour. The issue is that
- maybe a service method with setter-like name should not be skipped?
- this transform behavior should definitely be documented but I failed to find it in either GORM or Grails docs.
Workaround: I can either rename the method, add @Transactional
on the method directly or SystemConfiguration.withTransaction { ... }
within the method. Either is cool, but I'd like to know about this kind of thing beforehand - upgrade docs did not mention this either, just that proxies are out, transforms are in.
This is definitely intentional and setter methods should be skipped. This functionality has been around for a long time, but I agree it should be documented if its not already
I gotta give it to Grails / GORM teams, that Spring docs do not say that that setters are treated different either. Which breaks my heart as this is VERY specific logic and very important to know.
Even if one realizes the purpose of such logic (that setters may be used for bean configuration and most likely they should NOT contain business logic with would require transaction), that still should be stated, like in-your-face, in the docs wherever @Transactional
usage is described. Because from what I currently see:
that's for Grails 4.0.5
that's Spring 5.2
This point tripped me up for a long time today. So would very much appreciate a change to the doco to make it clearer that Transactions are not automatically applied to methods that have java-bean conventions if the @Transaction is only at the class level