How to define custom table mapping by Config.groovy entry in Grails - grails

I'm developing a plugin that needs a specific index configuration in table mapping.
static mapping = {
myProperty index:'myProperty_Idx'
}
Is it some way to let plugin users decide if they want to use or not this mapping via Config.groovy file?

I believe you can read the config variables right from your mapping block.
So this line in the app consuming your plugin's Config.groovy
grails.myplugin.useIndexForFoo = true
Should allow you to have a configurable domain class, such as
class Foo
{
String myString
static mapping = {
if (Holders.config?.grails?.myplugin?.useIndexForFoo)
myString index: "myString_idx"
}
}
Note I have used Holders rather than injecting grailsApplication bean because mapping config is static - do not know if this is optimal or not

Related

How do I inject service-like components to Grails domain objects?

I've never used Grails services before, but according to my Web searches, they can be injected into domain objects. So, if I have the following domain class (assume that BookService is defined somewhere),
class Book {
BookService bookService
}
when I instantiate a Book object using, def book = new Book(), book.bookService should be set?
If that's the case, what if I want to inject external services (or service-like components)? For example, if there's an ISBN API client available, can I expect grails to inject it the same way? For example, say the client is IsbnApi, how do I automatically inject like it's another service? Will the code below work?
class Book {
#Transient
#Autowire
IsbnApi isbnApi
}
Based on the earlier responses to this question, I've already defined the domain class this way:
class Book {
static transients = ['isbnApi']
IsbnApi isbnApi
}
Now, this may be all that I need, but I also want to test that the automatic injection would work even without having to run the app. In my unit tests, I have to set the mock "service" manually.
IsbnApi isbnApi = Mock()
Book book = new Book()
book.isbnApi = mockIsbnApi
I attempted to check automatic injection by moving the test to test/integration and set the beans using doWithSpring.
def doWithSpring() {
isbnApi = (InstanceFactoryBean, mockIsbnApi, isbnApi)
}
void "test automatic injection"() {
given:
IsbnApi isbnApi = Mock()
Book book = new Book()
//removed: book.isbnApi = mockIsbnApi
expect:
book.isbnApi //this fails
}
I also found additional information from this answer to a similar question. In particular,
Note that since you're using 'def' you don't need to add it to the
transients list. Are you trying to access it from a static method?
It's an instance field, so you can only access it from instances.
By convention Grails services are automatically candidates for dependency injection (= they can be injected into other classes). If you want to other classes you have to add them to conf/spring/resources.groovy.
E.g.:
import foo.bar.IsbnApi
beans = {
isbnApi(IsbnApi)
}
Have a look at the Bean Builder documentation section.
To make sure that your field is not treat as a persistent property, you can use the static transients field.
For example:
class User {
def grailsApplication
static transients = ['grailsApplication']
}
See transients documentation
In addition to the already accepted answers, as from grails 3.2.x you will need to enable autowiring on the domain ojbect in the mapping block or globally in the application config
class Book {
static transients = ['isbnApi']
IsbnApi isbnApi
static mapping = {
autowire true
}
}
It is disabled by default to improve read performance so perhaps it's best to find alternative approach instead of enabling it.
http://grailsblog.objectcomputing.com/posts/2017/05/09/injecting-a-service-into-a-domain-class.html

Grails 2.1.1 - Change default injected log object from commons.logging.Log to slf4j.Logger

I've a Grails project where I replaced Log4j with Logback. Now I also want to use the org.slf4j.Logger instead of org.apache.commons.logging.Log class for logging. How can I change the default log object which is placed by Dependency Injection to every Controller/Service/Domain class?
I searched within the BuildConfig.groovy and Config.groovy files but I couldn't find any configuration for this? I also looked at grails-app/src/templates/artifacts/Controller.groovy but I still couldn't find any place where this could be configured...
Shure, I could get my own instance of an org.slf4j.Logger, but then I'd have to declare it within every class like
private static Logger lbLogger = LoggerFactory.getLogger(MyClass.class)
and that's not what I want - I'd love to just replace injected Logger object.
Does anybody have any suggestions on this topic?
I can't recall how the log object been injected, but anyway you can inject your own logger into controller, service or domain object.
In BootStrap:
def doWithDynamicMethods = { applicationContext ->
// def logger = SLF4J new instance
application.controllerClasses.each { controllerClass ->
controllerClass.metaClass.getLogger = {-> logger }
}
}
This is the idea, sorry I don't have the Grails env now so I can not make a workable code.

Redefine domain class mapping at runtime

I was wondering if there is a way in groovy to change the static mapping section of a grails class at runtime. As of now my domain class looks like this:
class Result {
ObjectId id
String url
def Result(){
}
void addObjectProperty(String key, value){
this[key]=value
}
//No constrains defined yet.
static constraints = {
}
static mapWith="mongo"
static mapping = {
collection "results"
database "test"
}
}
Now lets just say I want to change the mapping section at runtime to:
static mapping = {
collection "xyz"
database "mydb"
}
Now when I call save() on an object it saves the result to mydb in the collection xyz. I bet there is a way in groovy to accomplish just that but since I'm new to groovy I'm having a hard time here ... it would be nice if someone could point me into the right direction.
Thanks a lot...
Note my comment above about the wisdom of doing this. That said, you can replace your mappings at runtime with Groovy's metaclassing functionality.
Result.metaClass.'static'.mapping = {
collection "myCollection"
database "myDatabase"
}
In Grails, the mapping block is a Groovy closure, so you're free to replace it with any other closure object whenever you'd like. This may have crazy unpredictable Hibernate side-effects or do nothing at all, as I do not know when the mapping closure is used to configure Hibernate in the Grails app lifecycle.

Prefix column names in GORM

with every project, I automatically run into a problem with reserved SQL word when I use properties like status or user in my Grails domain classes.
So I always have to add a
static mapping = {
status column:'prefix_status'
}
to my classes.
I now wonder if there is an easy way to prefix all columns with a given string?
If there is nothing out of the box, I guess it would be possible to create a plugin which automagically injects such a mapping in all domain classes - can someone point me to a code example which modifies a class whenever it changes?
This is already answered in the manual:
Object Relational Mapping (GORM) - Custom Naming Strategy
Add to DataSource.groovy Config:
hibernate {
...
naming_strategy = com.myco.myproj.CustomNamingStrategy
}
Custom Naming Class (under src/groovy/com/myco/myproj/CustomNamingStrategy.groovy):
package com.myco.myproj
import org.hibernate.cfg.ImprovedNamingStrategy
import org.hibernate.util.StringHelper
class CustomNamingStrategy extends ImprovedNamingStrategy {
String propertyToColumnName(String propertyName) {
"prefix_" + StringHelper.unqualify(propertyName)
}
}

Is it possible to metaprogram named queries onto a grails domain class?

Is it possible to metaprogram named queries onto a grails domain class? If so, how?
Thanks
Domain classes have a namedQueries property that you can use to add your own named queries. If you want to do this using metaprogramming from within a plugin (rather than by editing the domain class directly), you should do it in the doWithDynamicMethods closure of the plugin's descriptor file.
Something like this should work:
class MyPlugin {
def doWithDynamicMethods = { applicationContext ->
application.domainClasses.each { domainClass ->
boolean domainClassFilter = domainClass as Boolean
if (domainClassFilter) {
domainClass.metaClass.static.myNamedQuery = {->
// implementation of your named query goes here. Here is an example implementation
// that returns all instances with status == 'ready'
String simpleClassName = domainClass.simpleName
domainClass.findAll("from $simpleClassName where status = ?", ['ready'])
}
}
}
}
}
This will add myNamedQuery to every domain class in the application that the plugin is installed in. If you only want to add it to some domain classes, then replace the value of domainClassFilter with a more appropriate test.

Resources