Does using #Transactional disable the grails default transaction management - grails

According to the grails docs, services are transactional by default. But, I know you can get more fine grained control of transactions by using the Transactional attribute.
If I have a service such as
class MyService {
#Transactional(...config...)
def method1() { }
def method2() { }
}
My understanding is that in this case, method1 will be transactional, but method2 will not.
If I have
class MyService {
def method1() { }
def method2() { }
}
Then both method1 and method2 will both be transactional.
Is this correct?

If you want your service as transactional set to true the transactional property (this isn't obligatory but if you want to make clear that the service is transactional):
class MyService {
static transactional = true
def method1() { }
def method2() { }
}
If you don't want to:
class MyService {
static transactional = false
#Transactional(...config...)
def method1() { }
def method2() { }
}
Another example (setting transactional property isn't obligatory, but helps to be clear - if you are not the only coding this):
import org.springframework.transaction.annotation.Transactional
class BookService {
#Transactional(readOnly = true)
def listBooks() {
Book.list()
}
#Transactional
def updateBook() {
// …
}
def deleteBook() {
// …
}
}
Another thing you can do is annotate the whole class and override the methods you need to be different:
import org.springframework.transaction.annotation.Transactional
#Transactional
class BookService {
#Transactional(readOnly = true)
def listBooks() {
Book.list()
}
def updateBook() {
// …
}
def deleteBook() {
// …
}
}
Hope this helps ;)

You can disable the Grails default transaction management using withTransaction closure for domains to manage your Transaction manually as follows:
Account.withTransaction { status ->
try {
//write your code or business logic here
} catch (Exception e) {
status.setRollbackOnly()
}
}
If an exception is thrown, then the Transaction will be rollbacked.

Related

Existing transaction detected in JobRepository - Spring Batch with Grails Plugin

Every time I start my batch job it throws an IllegalStateException and says it detected a transaction in JobRepository. I did some research and removed all #Transactional annotations in my code.
I use the Grails Spring Batch Plugin you can find here, and I work with Grails 2.3.11 and Java 8. My code looks like this:
SimpleJobBatchConfig.groovy
beans {
xmlns batch:"http://www.springframework.org/schema/batch"
batch.job(id: 'simpleJob') {
batch.step(id: 'printStep') {
batch.tasklet(ref: 'printHelloWorld')
}
}
printHelloWorld(SimpleJobTasklet) { bean ->
bean.autowire = 'byName'
}
}
BatchTestController.groovy
class BatchelorController {
def batchTestService
def index() {
}
def launchSimpleJob() {
batchTestService.launchSimpleJob()
}
}
BatchTestService.groovy
class BatchTestService {
def springBatchService
def launchSimpleJob() {
springBatchService.launch("simpleJob")
}
}
SimpleJobTasklet.groovy
class SimpleJobTasklet implements Tasklet {
#Override
RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
println("Hello World!")
return RepeatStatus.FINISHED
}
}
Grails services are transactional by default. You can customize the settings for the whole class or per-method with #Transactional but if you have no annotations it's the same as having a class-scope Spring #Transactional annotation.
To make your service non-transactional, add static transactional = false, e.g.
class BatchTestService {
static transactional = false
def springBatchService
...
}
}

Grails integration test on a service: cannot set property on null object

I am using Grails 2.4.4 for my application, and using spock framework for tests.
class TaxpayerService {
static transactional = false
def springSecurityService
def setImporterDetails(TvfGen tvfGen) {
if (isTrader()) {
String taxPayerCode = springSecurityService?.principal?.getAt("tin")
def importerDetails = HistorizationSupport.withHistorizedFinder(BusinessLogicUtils.getWorkingDate(tvfGen)) {
Company.findByCode(taxPayerCode)
}
if (importerDetails) {
tvfGen.with {
impTaxPayerAcc = taxPayerCode
impName = importerDetails.name
}
}
}
}
}
Here is my integration test:
#Build([TvfGen])
class TaxpayerServiceSpec extends IntegrationSpec {
def taxpayerService
def springSecurityService
void setup() {
}
void tearDown() {
// Tear down logic here
}
void "test set importer details"() {
given:
def tvfGen = TvfGen.build()
springSecurityService = [principal: [tin: '0815790B', authorities: [ROLE_TRADER]]]
taxpayerService.springSecurityService = springSecurityService
taxpayerService.setImporterDetails(tvfGen)
expect:
tvfGen.impName == 'OMNI VALUE'
}
}
As a result I receive an error:
Cannot set property 'springSecurityService' on null object
java.lang.NullPointerException: Cannot set property 'springSecurityService' on null object
at TaxpayerServiceSpec.test set importer details(TaxpayerServiceSpec.groovy:34)

Grails code coverage not working

I am using Grails 2.2.4 and create JUnit Test for controller but code coverage not cover my test case of controller below are my test case detail.
//BuildConfig.groovy
plugins {
test ":code-coverage:2.0.3-3"
}
//MyControllerTests.groovy
#TestMixin(GrailsUnitTestMixin)
#TestFor(MyController)
#TestFor([Domain1,Domain2])
class MyControllerTests {
void setUp() {
controller.myService = new MyService()
}
void testAction1(){
controller.action1()
}
}
//mycontroller.groovy
class MyController {
def myService
def action1{
def msg = myService.myFirstAction()
}
}
//myservice.groovy
class MyService{
def myFirstAction(){
//logic which returns string
return 'my logic result'
}
}

idiom for save and update methods in grails

Are there in any idioms in grails which help us with saving domain objects ?
For example
i may want to do something like
if(candidate.hasErrors || !candidate.save)
{
candidate.errors.each {
log it
}
However i do not want to spread the logic across all the places i do domainObject.save.
I also do not want seperate class like say repo to which I pass this domainObject and put in this logic
Thanks
Sudarshan
Here's a service method that I've used to validate and save, but log resolved validation messages on failure. It's helpful to use this instead of just println error or log.warn error since the toString() for error objects is very verbose and you just want to see what would be displayed on the GSP:
class MyService {
def messageSource
def saveOrUpdate(bean, flush = false) {
return validate(bean) ? bean.save(flush: flush) : null
}
boolean validate(bean) {
bean.validate()
if (bean.hasErrors()) {
if (log.isEnabledFor(Level.WARN)) {
def message = new StringBuilder(
"problem ${bean.id ? 'updating' : 'creating'} ${bean.getClass().simpleName}: $bean")
def locale = Locale.getDefault()
for (fieldErrors in bean.errors) {
for (error in fieldErrors.allErrors) {
message.append("\n\t")
message.append(messageSource.getMessage(error, locale))
}
}
log.warn message
}
bean.discard()
return false
}
return true
}
And here's an example in a controller:
class MyController {
def myService
def actionName = {
def thing = new Thing(params)
if (myService.saveOrUpdate(thing)) {
redirect action: 'show', id: thing.id
}
else {
render view: 'create', model: [thing: thing]
}
}
}
Edit: It's also possible to add these methods to the MetaClass, e.g. in BootStrap.groovy:
class BootStrap {
def grailsApplication
def messageSource
def init = { servletContext ->
for (dc in grailsApplication.domainClasses) {
dc.metaClass.saveOrUpdate = { boolean flush = false ->
validateWithWarnings() ? delegate.save(flush: flush) : null
}
dc.metaClass.validateWithWarnings = { ->
delegate.validate()
if (delegate.hasErrors()) {
def message = new StringBuilder(
"problem ${delegate.id ? 'updating' : 'creating'} ${delegate.getClass().simpleName}: $delegate")
def locale = Locale.getDefault()
for (fieldErrors in delegate.errors) {
for (error in fieldErrors.allErrors) {
message.append("\n\t")
message.append(messageSource.getMessage(error, locale))
}
}
log.warn message
delegate.discard()
return false
}
return true
}
}
}
}
This depends on a 'log' variable being in scope, which will be true in any Grails artifact. This changes the controller usage slightly:
class MyController {
def actionName = {
def thing = new Thing(params)
if (thing.saveOrUpdate()) {
redirect action: 'show', id: thing.id
}
else {
render view: 'create', model: [thing: thing]
}
}
}
As a metaclass method it may make more sense to rename it, e.g. saveWithWarnings().

RuntimeError handling in Grails using View-Controller-Service architecture

I have following situation (simplified, of course):
MyDomain.groovy:
class MyDomain {
MyAnotherDomain anotherDomain // lazy loaded
}
MyService.groovy:
class MyService {
boolean transactional = true
def doSomething(id) {
// ... some code...
}
}
MYController.groovy:
class MyController {
def myService
def doAction = {
MyDomain aaa = ...GET_IT_FROM_SOMEWHERE...
try {
myService.doSomething(id)
} catch (RuntimeError e) {
flash.message = 'sorry.guy.your.transaction.was.rollbacked'
}
[myData: aaa]
}
}
doAction.gsp:
<html>
<body>
${myData.anotherDomain}
</body>
</html>
Problem happens when doSomething() throws RuntimeException. This RuntimeException rollback transaction and ends Hibernate session as well. When I render doAction.gsp after the RuntimeError, it ends with error, because lazy loaded field anotherDomain cannot be read (no session). Now you can say "ok, don't use RuntimeException", but I need the automatic transaction rollback.
Any ideas how to keep Hibernate session open even if RuntimeException happens in transactional service, so that lazy loading in gsp can render properly? Thanks.
If your Hibernate session is destroyed during rollback and xception throwing, you can try to manually reattach it to the current Hibernate session:
MyController.groovy:
class MyController {
def myService
def doAction = {
MyDomain aaa = ...GET_IT_FROM_SOMEWHERE...
try {
myService.doSomething(id)
} catch (RuntimeError e) {
flash.message = 'sorry.guy.your.transaction.was.rollbacked'
if(!aaa.isAttached()) {
aaa.attach()
}
}
[myData: aaa]
}
}
Hope that's suitable for your needs.
Reference

Resources