The class GrailsDomainClass has two methods: getProperties and getPersistentProperties
I have a domain class (MyDomainClass) which includes this:
static transients = {computeStuff}
float computeStuff(){
def thisCouldChange = 3.1
return thisCouldChange //it actually does things but not of consequence to my question
}
Ok, I took the default index page and modified it to list all the properties of MyDomainClass as such:
<g:each var="d" in="${grailsApplication.domainClasses.sort { it.fullName } }">
<h2>${d.fullName}</h2>
<g:each var="f" in="${d.properties.sort { it.fieldName } }">
<br>${f.fieldName }
</g:each>
</g:each>
Ok. That works, but it doesn't get any of the transient properties. I have tried d.properties and d.persistantProperties and they seem to give me back the same results. Thanks in advance for your help!!
Do I need to call it getComputeStuff or something?
I have now changed my domain class to contain this and still do not get back the transient computeStuff
static transients = ['computeStuff']
float getComputeStuff(){
def thisCouldChange = 3.1
return thisCouldChange //it actually does things but not of consequence to my question
}
This seems to have made no difference.
remove the static transient decleration. define your method as follows:
def getComputeStuff(){
def thisCouldChange = 3.1
return 3.1 //it actually does things but not of consequence to my question
}
after that a property call "computeStuff" should appear in your properties list calling getProperties() on the domain class. its very important that you define the return value as def.
I think that if you want your computeStuff method be treated like a property, call it getComputeStuff - name convention as JavaBeans. I'm not sure id it will work, but it's worth a try I think ;)
Related
I am using Grails 2.5 and use Grails databinding in request methods.
For a basic example of the situation consider the following:
Domain class
class Product {
String field1
String privateField
}
Controller
class ProductController {
def update(Product productInstance) {
productInstance.save()
}
}
If I pass an existing Product to the controller like
{"id":3, "privateField":"newValue","field1":"whatever"}
the old value of privateField is overwritten. I want to enforce, that privateField is never bound from a request and avoid checking if the field is dirty.
Is there a mechanism in Grails to achieve this?
If I have to do the dirty check, how can I discard the new value and use the old one?
Pretty sure there's a "bindable" constraint.
http://grails.github.io/grails-doc/2.5.x/ref/Constraints/bindable.html
class Product {
String field1
String privateField
static constraints = {
privateField bindable: false
}
}
Should keep that field from binding automatically.
You can enforce which values are bound, but you'll need to change your method signature to get more control of the data binding process.
class ProductController {
def update() {
def productInstance = Product.get(params.id)
bindData(productInstance, params, [exclude: ['privateField']]
productInstance.save()
}
}
I've two domain classes
1.CustomerInterest.groovy
static hasMany = [activities:Activity]
static belongsTo=[customer:Customer,projectProperty:ProjectProperty]
static mapping={
activities sort:'dateCreated',order:'desc'
}
2.Activity.groovy
Date dateCreated
static belongsTo = [customerInterest:CustomerInterest, employee:Employee]
In a controller i am doing this..
def customerDetails(Customer customer)
{
def customerInterest=customer.customerInterests
render view:"customerDetails",model:[customerInterest:customerInterest]
}
customerDetails.gsp
<g:each in="${customerInterest}" var="ci">
${ci}
</g:each>
**
Question: I want to sort CustomerInterest on property dateCreated of Activity domain.
**
Any help as soon as possible would be appreciated.
Try this
def customerInterest=customer.customerInterests.sort { it.activities.dateCreated }
Instead of using the sort order in the mappings definition, you can use the old java Comparable-interface:
class Foo implements Comparable {
int compareTo(anotherObject) {
// complex logic returning -1 or 0 or 1
}
}
Then you can call listOfFoos.sort() and it will sort it with the help of the compareTo-method.
Beware that (equals) == will also use this method, so ensure that (only) identical objects will return 0.
I've got several properties in my domain class. However, I only want few of them to be set via the params object. What is a good way to do this?
Example:
Domain
class Color {
String name
String shade //don't want this set by params
}
controller
class ColorController {
def save() {
json {
def c = new Color(params?.color)
c.save(flush: true)
//..more code
}
}
}
If someone sends a request like:
{"color":
{name: "red",
shade: "light"
}
}
then user can change the shade property. How can I stop this?
You could probably do one of a couple of things:
If it is many properties, create a transient beforeInsert() {} and/or transient beforeUpdate() {} method in your domain class and handle setting (or not) the properties.
If only a few, override the setters in the domain class.
Since Groovy makes me not want to mess with getters and setters unless I absolutely have to, I usually use the beforeInsert and beforeUpdate methods.
Grails provides a bindData method on the controller to give you fine grained control of data-binding. For your example you could write this as:
class ColorController {
def save() {
json {
def c = new Color()
bindData(c, params, [include: 'name'])
c.save(flush: true)
//..more code
}
}
}
In this case, only the 'name' field would be set on the c instance before attempting to save.
If you want to to additional validation on the incoming params, I would also suggest looking into using a Command Object for the data binding.
Hey all, I'm building a GRAILS application with a m:m db relation. As I try to show the entries the well known "failed to lazily initialize a collection of role ... no session or session was closed" error is shown.
One class is:
class Hazzard{
static hasMany = [warning:Warning]
static constraints = {
text(size:1..5000)
}
String name
String text
String toxicity
}
the other:
class Warning{
static hasMany = [hazzard:Hazzard]
static belongsTo = Hazzard
static constraints = {
text(size:1..5000)
}
String code
String text
}
In Hazzard/show the following code works fine
<g:each in="${hazzardInstance.warning}" var="p">
<li><g:link controller="Warning" action="show" id="${p.id}">${p?.encodeAsHTML()}</g:link></li>
</g:each>
but on other pages the following code will provide the error:
<g:set var="haz" value="${Hazzard.get(params.id)}" />
<h1>${haz.name}</h1>
<p>${haz.text}</p>
<h1>Toxiciteit</h1>
<p>${haz.toxicity}</p>
<br/>
<h1>Gevaren(H) en voorzorgen(P)</h1>
<g:each in="${haz.warning}" var="p"> --> This is where the error pops-up
${p.text}
</g:each>
Any clues on where this fails?
A more favorable approach to what you're trying to do would be to perform the get in the controller and pass the found domain object to the view for rendering. Something like:
// MyController.groovy
class MyController {
def myAction = {
def haz = Hazzard.get(params.id)
render(view: 'myview', model: [hazzardInstance: haz])
}
}
// my/myview.gsp (the view from your second GSP code block)
<h1>${hazzardInstance?.name.encodeAsHTML()}</h1>
...
<h1>Gevaren(H) en voorzorgen(P)</h1>
<g:each in="${hazzardInstance?.warning}" var="p">...</g:each>
Doing GORM lookups in the view can sometimes lead to the exception you're getting, although I thought many problems like that had been fixed in more recent versions of Grails. Nonetheless, using a more correct idiom for querying and rendering views will help you to avoid this error.
I get the following Exception when saving an instance of Trip in Grails:
2011-01-26 22:37:42,801 [http-8090-5]
ERROR errors.GrailsExceptionResolver
- object references an unsaved transient instance - save the
transient instance before flushing: Rower
org.hibernate.TransientObjectException:
object references an unsaved transient
instance - save the transient instance
before flushing: Rower
The concept is simple: For a boattrip you need some rowers, a coxwain (is also a rower) and a boat:
Trip looks like (shortened):
class Trip {
Boat boat;
Rower coxwain;
static belongsTo = [Rower,Boat]
static hasMany = [rowers:Rower]
}
and Rower (shortened)
class Rower {
String firstname;
String name;
Rower reference;
static hasMany = [trips:Trip];
static mappedBy = [trips:"rowers"]
}
The trip then is saved in the controller like:
def save = {
def trip = new Trip(params)
// adding Rowers to Trip
if (params.rower instanceof String) {
def r = Rower.get(params?.rower)
if (r != null) {
trip.addToRowers(r)
}
} else {
params?.rower?.each{
rowerid ->
def r = Rower.get(rowerid)
log.info("rowerid (asList): " + rowerid)
if (r != null) {
trip.addToRowers(r)
}
}
}
// saving the new Trip -> EXCEPTION IN NEXT LINE
if(!trip.hasErrors() && trip.save(flush:true)) {
// ...
}
// ...
}
I think I have set the relations between the domains correct.
The Rower is not changed while it is added to the Trip. Why does Grails want it to save? why is it a transient instance?
Unfortunately this is an issue with the way GORM handles things, or more specifically the way that it expects that you deal with transients. If you don't persist the contained classes to the database first (Rowers in this case), you will get this exception every single time.
With GORM you have to save and attach in a bottom up fashion or when grails goes to flush the connection for the next instance you will get the transient instance exception. The instance is 'transient' because its just an in-memory reference. To persist the parent, GORM needs to link the parent to the child in the database. Without the child being persisted it has no way to do that, this is where the exception is coming from.
Wish there was better news. Not that its hard, but it gets annoying with complex hierarchies.
The Problem was somehow different.
it's in here:
def trip = new Trip(params)
which references a coxwain (of class Rower), which is not set (id=-1 is returned). This constructs a new Rower instead of a 'null' value. And this is the 'unsaved transient instance'.
If I check first for a valid instance, then it works :-)
Thanks for the help!
Just a quick note for anyone dealing with singular or multiple parameters with the same name, using the params.list("parameterName") helper you can always return a list
...
// adding Rowers to Trip
if (params.rower instanceof String) {
def r = Rower.get(params?.rower)
if (r != null) {
trip.addToRowers(r)
}
} else {
params?.rower?.each{
rowerid ->
def r = Rower.get(rowerid)
log.info("rowerid (asList): " + rowerid)
if (r != null) {
trip.addToRowers(r)
}
}
}
...
could become a bit groovier
...
// adding Rowers to Trip
for(rower in params.list("rower") {
def r = Rower.get(rower)
if(r) trip.addToRowers(r)
}
...
you can find it hiding away under 6.1.12 Simple Type Converters
At first I thought it was to do with cascading saves and belongsTo, as described in The Grails Reference section 5.2.1.3 and Gorm Gotchas part 2. However since the Rowers are already in the DB I think it should work. The domain model is complicated to me, what you need to do is simplify it and run some tests using Grails console (run grails console in your project directory). First create a basic many-to-many between Trip and Rower and get it to execute the desired code. Then add the other parts bit-by-bit, like Rower's reference to itself. I'm not sure that the mappedBy part is necessary at all.
I think you have to save the trip before you add the rover to the trip.
Also it make no sense to check if trip has errors before you validate and/or save it.
Try this:
if(trip.validate() && trip.save(flush:true)) {
if (r != null) {
trip.addToRowers(r)
}
}
In many use cases, you should be able to address this by applying the cascade setting to your collection:
static mapping = {
rowers cascade: 'all-delete-orphan'
}
https://docs.grails.org/latest/ref/Database%20Mapping/cascade.html
Suppose, you are using Trip is hasMany relationship with Rower
class Trip {
static hasMany = [rowers:Rower]
...
}
class Rower{
static belongsTo =[trips:Trip]
...
}
Actually, it's looking for new session. So we need to stop/rollback the current transaction then this error won't raise, For that
Try This,
def row = new Row()
row.save()
NOTE: this save won't affect your database. It's just for rollback the transaction.
I had this problem but the answer for me was much simpler - my first object had not saved properly because it didn't validate successfully.
I somehow didn't notice that the first object had not saved, and so the error was confusing to me.
While debugging the problem, I eventually noticed that the first object was not saving properly, and when I fixed that, the object references an unsaved transient instance problem went away.