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
Related
I want to implement the concept of Trigger objects in Grails for my domain classes. Grails allows me to define methods like beforeInsert() in my domain classes to handle insert, update and delete events, similar to how SQL databases have the concept of database triggers. But to separate concerns I'd like to move all of the trigger logic outside of the domain class.
Ideally I can simply define a static list of trigger classes in my domain class and then iterate through those in the beforeInsert() method. Sample code shown below.
static beforeInsertTriggers = [AccountNameTrigger, AccountDumpTrigger]
def beforeInsert() {
for (Class<Trigger> triggerClass : beforeInsertTriggers) {
triggerClass.newInstance().using(this).execute()
}
}
I've created a sample Grails 2.5.4 project on GitHub illustrating what I'm trying to do. But the problem is that the triggers literally are standalone islands of logic without the ability to autowire services. How can I better set up this Trigger pattern or something similar? So I may autowire services and other beans into the Trigger instances?
I am also trying to keep the design simple and readable.
Avoid cluttering the domain class with fields and properties that are not related to the data model. To me this means no autowired beans in the domain class.
Define the list of triggers using the trigger class names (or bean names, if need be). This may be overly idealistic, but hey...
For what it's worth, part of my inspiration also comes from how Salesforce implements triggers as distinct units of self-contained code.
Register your Triggers as Singleton Beans and there you can inject other services/beans. You can create your custom beans via resources.groovy.
Lets take the example of AccountDumpTrigger. Lets make a simple change to it:
package grails.domain.trigger.demo.triggers
import grails.domain.trigger.demo.Account
import org.codehaus.groovy.grails.commons.GrailsApplication
/**
* Created by marty on 6/25/16.
*/
class AccountDumpTrigger extends AbstractTrigger<Account> {
GrailsApplication grailsApplication
#Override
void execute() {
println grailsApplication.isInitialised()
println resource.dump()
}
}
And code in resources.groovy or in your plugin's doWithSpring closure:
accountDumpTrigger(grails.domain.trigger.demo.triggers.AccountDumpTrigger) { bean ->
bean.factoryMethod = 'getInstance'
/*
either refer each bean individually or you can use:
bean.autowire = "byType"
OR
bean.autowire = "byName"
*/
grailsApplication = ref("grailsApplication")
}
And inside your domain:
static beforeInsertTriggers = [AccountDumpTrigger]
def beforeInsert() {
for (Class<Trigger> triggerClass : beforeInsertTriggers) {
triggerClass.instance.using(this).execute()
}
}
And instead of writing your code inside beforeInsert you can also do the same by registering an implementation of AbstractPersistenceEventListener. This way you don't have to repeat your code. Also yo can move it to a parent class.
I have two domain classes as follows:
class Field {
static hasMany = [options: Option]
}
class Option {
String caption
static belongsTo = [field: Field]
static mappedBy = [field: Field]
static constraints = {
caption maxSize: 255
}
}
My question is: When do I use field.options and when do I use Options.findAllByField()? My unit tests which were using field.options (grails injected property types) are now starting to fail after switching to HibernateTestMixin (which uses an in memory database). They will only pass if I switch to the Gorm dynamic finders. What am I doing wrong? :(
Thanks.
One thing, that is wrong is the usage of mappedBy. You should use this for associations, if you have multiple properties of the same type. I suggest to remove the part:
static mappedBy = [field: Field]
But this will most probably not solve your issue.
If you have an field instance (attached to the hibernate session), then you should use the natural:
Set options = field.options
When your field instance gets detached from hibernate session and its options association is not loaded (because of lazy-loading), then issuing field.options will throw LazyInitializationException.
Please post the error you get, when using field.options in your tests. Maybe your field instance got detached from the session (which could be caused by RuntimeException thrown from a transactional service).
I have a requirement, my current project is with ejb3, jpa and jsf. I have a core development project and customer specific project. In the customer specific project, we planned to inherit the core classes and extend or override the core functionality. So we planned to create the customer specific classes with some customer prefix. So in the final war file, we will have the core class and all customer prefix classes. For example, if Authenticator is a core class and XXAuthenticator, YYAuthenticator are customer specific classes exist in the build. So, for example if i have a line of code in a bean like this:
#Inject Authenticator authenticator;
Can i programmatically and/or dynamically inject the inherited classes based on some logic like logged in user has a customer specific functionality.
what i am expecting is, i dont want to change the above line to inject, because it will be big change in every class. But expecting some kind of dynamic logic or configuration file to change the core class injection to customer specific class..
So finally with out touching the #Inject Authenticator authenticator; line. Can I inject the xxAuthenticator or YYAuthenticator through some logic? We dont have Spring in project technology stack.. So please suggest me with out spring only.. Thanks in advance
It sounds like your use case is more around Qualifiers. If a user follows a certain, you should be injecting different qualified versions of classes to use, no?
#Inject
private Instance<SomeService> someServiceInstance;
// later on...
SomeService someService = null;
if(someCondition) {
someService = someServiceInstance.select(new FooLiteral()).get();
}
else {
someService = someServiceInstance.select(new BarLiteral()).get();
}
Where FooLiteral and BarLiteral are annotation literals for #Foo and #Bar, which are qualifiers.
In CDI this is done with "producer methods".
It would look like this:
#ApplicationScoped
public class AuthenticatorProducer {
private Authenticator xxAuthenticator; // = ...
private Authenticator yyAuthenticator; // = ...
#Produces
public Authenticator getAuthenticator() {
if (someCondition) {
return xxAuthenticator;
} else {
return yyAuthenticator;
}
}
}
No need to change injection points and Authenticators don't have to be CDI beans themselves.
Given these functional requirements:
User Management
Administrator
Librarian
Borrower
*The users have the option of logging-in via OpenID.
Property Management
Book
Memorandum
Circular
License
Normally, I would implement these in Java as:
interface User {}
class Librarian implements User {}
class Administrator implements User {}
class Borrower implements User {}
class OpenID {} //all Users HAS AN OpenID attribute (NULL if non-openId login)
interface Property{}
class Book implements Property{}
class Memorandum implements Property{}
class Circular implements Property{}
class License implements Property{}
But our project will use Groovy & Grails, which I haven't experience using yet. My question is,
how should the domain classes be designed based on the requirements above? I can't use an interface, and it seems inheritance is not a good practice. My idea is to use composition, though I'm quite bothered by the database tables that would be generated. What are the best practices in this situation?
Well first of all lets correct it, you can use inheritance in this case. You just need to change the convention of has a relationship to is a relationship.
Few factors to keep note of:
1. Grails works on convention over configuration.
2. You can use GORM which wraps the persistence layer and creates an Object Mapping for the underlying persistence layer with the help of Hibernate.
As per your functional requirement:-
If you do not want to have the User as part of persistence you can have an abstract class User which can hold the common properties of the User including the openId attribute. It has to be placed in src\groovy directory as per convention (since the base class is abstract, dependency injection will be defied)
The same goes for Property. Abstract Property class in src\groovy.
Now coming to the business models, extend each of the concrete entities (domain classes) from the abstract parent.
Summary:-
Create grails app
Under src\groovy(for example, I am considering a basic structure):
User.groovy:-
abstract class User{
String name
String emailId
OpenID openId
}
Property.groovy:-
abstract class Property{
String propertyName
}
Under grails-app/domain:
Librariran.groovy:-
class Librarian extends User{
//Attributes specific to Librariran
static constraints = {
}
static mapping = {
}
}
Book.groovy:-
class Book extends Property{
//Attributes specific to Book
static constraints = {
}
static mapping = {
}
}
So on and so forth. Groovy objects under grails-app/domain are considered concrete entities by Grails convention. More information you can obviously find here. You can also use composition if you come across scenarios, in fact I already mentioned that in User having OpenId.
Note:- This is context to latest version of Grails (> 2.x)
What's the best/easiest way to get a list of the persistent properties associated with a given GORM domain object? I can get the list of all properties, but this list contains non-persistent fields such as class and constraints.
Currently I'm using this and filtering out the list of nonPersistent properties using a list I created:
def nonPersistent = ["log", "class", "constraints", "properties", "errors", "mapping", "metaClass"]
def newMap = [:]
domainObject.getProperties().each { property ->
if (!nonPersistent.contains(property.key)) {
newMap.put property.key, property.value
}
}
There seems like there must be a better way of getting just the persistent properties.
Try this:
import org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass
...
def d = new DefaultGrailsDomainClass(YourDomain.class)
d.persistentProperties
Here's a link to the Grails API for GrailsDomainClass (it's a link to an older version; I couldn't find a newer one after some quick searches). It's got a getPersistentProperties() (used in the code snippet above). You can traverse the API documentation to see what other methods might be useful to you.
If you want an example, do a grails install-templates and then look at src/templates/scaffolding/create.gsp. There's a block in there where it iterates over the persistent domain properties.
Now (strarting Grails 2.x) you don't even have to instantiate new DefaultGrailsDomainClass(...) and avoid unnecessary code executions. All domain class objects have injected property domainClass:
def domainObject = new YourDomain()
domainObject.domainClass.persistentProperties
Or, if you haven't domain class object, you can get DefaultGrailsDomainClass from application context by domain class name - each domain class has a DefaultGrailsDomainClass registered as a Spring bean. So you can use, for example, Holders (assuming your domain class name is 'Foo'):
def defaultGrailsDomainClass = Holders.applicationContext.getBean("FooDomainClass")
defaultGrailsDomainClass.persistentProperties
As of grails 3.3.0
All code that uses the GrailsDomainClass or GrailsDomainClassProperty classes should be re-written to use the mapping context api.
To get started, inject the grailsDomainClassMappingContext bean. See the api documentation for more information on the MappingContext, PersistentEntity (GrailsDomainClass), and PersistentProperty(GrailsDomainClassProperty)
For example:
class MyService {
def grailsDomainClassMappingContext //inject
def accessDomainProperties(Class clazz) {
PersistentEntity entityClass = grailsDomainClassMappingContext.getPersistentEntity(clazz.name)
List<PersistentProperty> persistentPropertyList = entityClass.persistentProperties
persistentPropertyList.each { property ->
println property.name
}
}
}
Hope this helps someone.