Grails Reusable Service for saving Domain Objects - grails

I have a Grails project with multiple Domain Classes, and I want to make a persistence service as reusable as possible by only having one save() inside of it. To try and achieve this I have done the following in my project.
//PersistenceService.groovy
#Transactional
class PersistenceService {
def create(Object object) {
object.save flush: true
object
}
//BaseRestfulController
class BaseRestfulController extends RestfulController {
def persistenceService
def save(Object object) {
persistenceService.create(object)
}
//BookController
class BookController extends BaseRestfulController {
private static final log = LogFactory.getLog(this)
static responseFormats = ['json', 'xml']
BookController() {
super(Book)
}
#Transactional
def save(Book book) {
log.debug("creating book")
super.save(book)
}
So basically I have a bunch of domains for example Author etc, each with their own controller similar to the bookController. So is there a way to reuse the service for persistence like I am trying above?
Thank you

I'm doing something similar, but mainly because all my entities are not actually removed from the database but rather "marked" as removed. For several apps you need such an approach since it's critical to prevent any kind of data loss.
Since most databases do not provide support for this scenario, you can't rely on foreign keys to remove dependent domain instances when removing a parent one.
So I have a base service class called GenericDomainService which has methods to save, delete (mark), undelete (unmark).
This service provides a basic implementation which can be applied to any domain.
class GenericDomainService {
def save( instance ) {
if( !instance || instance.hasErrors() || !instance.save( flush: true ) ) {
instance.errors.allErrors.each {
if( it instanceof org.springframework.validation.FieldError ) {
log.error "${it.objectName}.${it.field}: ${it.code} (${it.rejectedValue})"
}
else {
log.error it
}
}
return null
}
else {
return instance
}
}
def delete( instance, date = new Date() ) {
instance.dateDisabled = date
instance.save( validate: false, flush: true )
return null
}
def undelete( instance ) {
instance.dateDisabled = null
instance.save( validate: false, flush: true )
return null
}
}
Then, in my controller template I always declare two services: the generic plus the concrete (which may not exist):
def ${domainClass.propertyName}Service
def genericDomainService
Which would translate for a domain called Book into:
def bookService
def genericDomainService
Within the controller methods I use the service like:
def service = bookService ?: genericDomainService
service.save( instance )
Finally, the service for a given domain will inherit from this one providing (if needed) the custom logic for these actions:
class BookService extends GenericDomainService {
def delete( instance, date = new Date() ) {
BookReview.executeUpdate( "update BookReview b set b.dateDisabled = :date where b.book.id = :bookId and b.dateDisabled is null", [ date: date, bookId: instance.id ] )
super.delete( instance, date )
}
def undelete( instance ) {
BookReview.executeUpdate( "update BookReview b set b.dateDisabled = null where b.dateDisabled = :date and b.book.id = :bookId", [ date: instance.dateDisabled, bookId: instance.id ] )
super.undelete( instance )
}
}
Hope that helps.

Related

Share variable across domain and controller in grails

can i have one variable shared between two classes in grails, like in my controller i want to set a variable processStart as true, and once the after save method is done in my domain class of that controller i want to set it false like this,
class EmployeeController{
def insert() {
for (i in 1..params.numberOfEmp.toInteger()) {
Employee emp = new Employee(params)
processStart = true // set this variable here
emp.save()
}
}
}
and in domain class
class Employee {
/** domain structure **/
def afterInsert () {
processStart = false // and after this, set this variable here
}
}
Try using a session variable, you shouldn't do this with a static variable.
class EmployeeController{
def insert() {
for (i in 1..params.numberOfEmp.toInteger()) {
Employee emp = new Employee(params)
session['processStart'] = true // set this variable here
emp.save()
}
}
}
and in domain class:
class Employee {
/** domain structure **/
def afterInsert () {
session['processStart'] = false // and after this, set this variable here
}
}

Grails console plugin (unexpected results)

I have the two domain clases:
class Persona {
String nombre
String apellidos
static hasMany = [pertenencias: Pertenencia]
static constraints = {
}
static mapping = {
pertenencias cascade: "all-delete-orphan"
}
}
class Pertenencia {
String nombre
static belongsTo = [persona:Persona]
static constraints = {
}
}
The service:
class MembresiaService {
#Transactional
def saveAll() {
def p = new Persona(nombre: 'carlos', apellidos: 'gm')
p.addToPertenencias(nombre: 'auto')
p.addToPertenencias(nombre: 'computadora')
p.addToPertenencias(nombre: 'casa')
p.save()
}
#Transactional
def deletePertenencias() {
def p = Persona.get(1)
p.pertenencias?.clear()
}
}
And the controller:
class TestController {
def membresiaService
def index() {}
def saveAll() {
membresiaService.saveAll()
redirect(action: "index")
}
def deletePertenencias() {
membresiaService.deletePertenencias()
redirect(action: "index")
}
}
When I execute saveAll() method from controller it saves the data in the database, when I execute deletePertenencias() from controller it deletes the "pertenecias" collection of Persona from the database (as expected).
I have installed the Grails console plugin , first time I execute the lines of saveAll() service method in the console, the result is the "persona" and its "pertenencias" in database. Then I execute the lines of deletePertenencias() service method in console but it doesn't delete the data of database and the "persona" object mantains the "pertenencias" (as if I had not run deletePertenencias() code).
Anyone kwnow why the code executed from console gives unexpected results?
I expect the result was the same from controller and console but the behaviour is different.

Grails create instance of inner class of service

In Grails, services are singletons by default. Can I keep it that way and still create an instance of an inner class of that service from a controller?
//by default grails makes MyTestService a singlton
class MyTestService{
public class InnerTest{
String msg;
def addMsg(String str){
this.msg=str;
}
def printMsg(){
println this.msg;
}
}
}
In controller "MyController"...
def m=myTestService.getInstance().new InnerTest();
//produces " MyTestService.InnerTest cannot be cast to MyTestService.InnerTest"
def m=myTestService.new InnerTest();
//No signature of method:MyController.InnerTest()
You should be able to do something like:
class MyTestService{
public class InnerTest{
String msg;
def addMsg(String str){
this.msg=str;
}
def printMsg(){
println this.msg;
}
}
def InnerTestFactory() {
new InnerTest()
}
}
And use it from your controller:
def m=myTestService.InnerTestFactory();

Repeating code using Grails domain find method

Initial Problem
If you have different methods that basically have only one line different, would there be a way to make it DRY by creating one method.
Example:
def showA( ) {
def instance
try {
instance = A.findById( params.id )
} catch ( Exception e ) {
def message = "Error while retrieving details for the given id ${ params.id }, $e"
log.error message
responseAsJson( 400, "Invalid id", message )
return false
}
return checkAndRender(instance, params.id);
}
def showB( ) {
def instance
try {
instance = B.findByBId( params.BId )
} catch ( Exception e ) {
def message = "Error while retrieving details for the given id ${ params.id }, $e"
log.error message
responseAsJson( 400, "Invalid id", message )
return false
}
return checkAndRender(instance, params.id);
}
So, would there be a way to make one method and simply pass as parameter:
The domain class
the ID to search for
Or would it be better to pass an SQL statement instead?
Update
Based on #dmahapatro comment, I came up with the following:
def showA( ) {
def clos = {id -> A.findByAId( id ) }
return findAndShow(clos, params.AId, params )
}
def showB( ) {
def clos = {id -> B.findByBId( id ) }
return findAndShow(clos, params.BId, params )
}
def findAndShow(Closure closure, def id, def p)
{
def instance
try {
instance = closure(id)
}
catch ( Exception e ) {
def message = "Error while retrieving instance details for the given id ${ id }, $e"
log.error message
responseAsJson( 400, "Invalid Id", message )
return false
}
return checkAndRender(instance, id);
}
Only remaining issues are:
How to cleanup even further / make it cleaner.
How to bypass warning:
The [findAndShow] action in [ApiController] accepts a parameter of
type [groovy.lang.Closure]. Interface types and abstract class types
are not supported as command objects. This parameter will be ignored.
def findAndShow(Closure closure, def id, def p)
First thing you should worry if you want a DRY code, is define a better exception handling. Try-catching your code everywhere to handle response to the client is not very DRY, if you put your data-access code in services, you can throw exceptions from them and use a global controller for catch the errors and handle the responses. E.g:
class ErrorController {
def serverError() {
if (request.format == 'json') {
//Code for handling errors in json request, request.exception stores the data about the exception.
} else {
//Code for handling errors in non-json request, e.g:
render(view: 'error', model: [msg: 'Something went wrong']) //add an error view for this
}
}
}
If you like, you can also add handlers for other types of errors (403, 404, etc)
Add to UrlMappings.groovy
"500"(controller: "error", action: "serverError")
Now you can refactor your code using your new error handling, and reflection:
Controller:
class MyController {
def myService
def show() {
def result = myService.myFind(params.className,params.id)
render result as JSON //Render stuff
}
}
Service:
import grails.util.Holders
class MyService {
def myFind(String className, Long id) {
def result = Holders.getGrailsApplication().getDomainClass('com.mypack.'+ className).findById(id)
if(!result) {
throw new ServiceException('really descriptive and usefull error msg')
}
}
}
I defined a ServiceException class so i can add custom logic for it in my ErrorController using the instanceOf operator.

Grails share a GSP with multiple user

I having a GSP when it is accessed by two or more user at same time, It will throw
Row was updated or deleted by another transaction
Is there any way to share the render safely
Note: There is no update or save operation while rendering
Dude ,Have you tired on the action who updates ,list and read or connected with the row can be
#Transactional or do some concurrencyStuff exception handling
//Assume your domains are implemented just like or to be like this
class Author {
String name
Integer age
static hasMany = [books: Book]
}
//option one in your controllers or service class
Author.withTransaction { status ->
new Author(name: "Stephen King", age: 40).save()
status.setRollbackOnly()
}
Author.withTransaction { status ->
new Author(name: "Stephen King", age: 40).save()
}
//or
#Transactionl
def AuthorController () {
[list:list]
}
//or define a service like this
import org.springframework.transaction.annotation.Transactional
class BookService {
#Transactional(readOnly = true)
def listBooks() {
Book.list()
}
#Transactional
def updateBook() {
// …
}
def deleteBook() {
// …
}
}
for more visit : I hope this will be a bit useful ,Buddy!

Resources