grails-data-mapping icon indicating copy to clipboard operation
grails-data-mapping copied to clipboard

@Transactional not applied to setter-like service method

Open zoladkow opened this issue 6 years ago • 3 comments

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

  1. Create a grails service with @Transactional annotation
  2. Create a method on that service called setSystemConfiguration(List<...> configuration) which should iterate the list an persist new instances of SystemConfiguration. No actual property like that exists on the service. Well it's a service, it does stuff, not hold stuff!
  3. 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.

zoladkow avatar Dec 05 '18 09:12 zoladkow

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

jameskleeh avatar Dec 05 '18 15:12 jameskleeh

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 image

that's Spring 5.2 image

zoladkow avatar Dec 18 '20 21:12 zoladkow

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

boardbloke avatar Sep 05 '22 13:09 boardbloke