Unexpected transactional behavior when defining GORM data service as abstract class
- Dominant language
- Groovy
- Stars
- 2.9k
- Forks
- 975
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 92
Description
The documentation says this regarding how transactions will be applied when defining a GORM data service as an abstract class:
> In addition, all public methods of the domain class will be automatically wrapped in the appropriate transaction handling.
The behavior I am seeing is different though. I have two examples here to illustrate.
In this first example, the createOrUpdate method is not wrapped in a transaction unless I add the @Transactional annotation at the class level, which the documentation says I should not have to do.
```groovy
@Service(User)
@Transactional // without this, createOrUpdate is not transactional!
abstract class UserService {
abstract User get(Serializable id)
abstract List list(Map args)
abstract Long count()
abstract void delete(Serializable id)
abstract User save(User user)
User createOrUpdate(User user, Set roles) {
user = this.save(user)
if (roles != null && !roles.isEmpty()) {
// delete any existing user role mappings
UserRole.removeAll(user)
// add new user role mappings
for (Role role : roles) {
UserRole.create(user, role)
}
}
return user
}
}
```
In this second example, something even stranger is happening. The save method is not wrapped in a transaction unless I add an annotation specifically for the save method (the class level annotation is not enough).
```groovy
@Service(ReplenishmentRequest)
@Transactional
abstract class ReplenishmentRequestService {
@Autowired
SpringSecurityService springSecurityService
abstract ReplenishmentRequest get(Serializable id)
abstract List list(Map args)
abstract Long count()
abstract void delete(Serializable id)
@Transactional // without this, save is not transactional!
ReplenishmentRequest save(ReplenishmentRequest replenishmentRequest) {
// details elided
}
}
```
I haven't yet tried to walk through the entire sequence at runtime to see what is going wrong, but the behavior does not match the documentation. It is also surprising that in the second case the class level annotation is not enough to make the save method transactional. Any insight you can provide is greatly appreciated.
Contributor guide
Research direction
Start by reproducing the behavior in the UserService and ReplenishmentRequestService examples, comparing class-level and method-level @Transactional annotations. Trace how the abstract GORM data service methods are wrapped and verify whether createOrUpdate and save run transactionally without extra annotations. Done means the implementation matches the documented transaction behavior or the documentation clearly describes the actual behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- groovy
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100