Grails - Can't add child record to parent - grails

Trying to follow the example shown here I am trying to create a record which links to a parent record.
In my case I have two classes: Sensor, and Readings. I can create Sensors without any issues, but no matter how I try to create readings I seem to fail :(
I've been spinning my wheels for long enough, I'm throwing in the towel and hoping someone can spot my silly mistake(s).
One more thing - I want to post the data using JSON. But through the debugging process I'm not even looking at the JSON values, I've hard coded them and it still doesn't work.
ReadingsController.groovy
package grailshelloworld
import grails.converters.JSON
import groovy.json.JsonSlurper
class ReadingsController {
def scaffold=Readings
def save = {
def slurper = new JsonSlurper()
def result = slurper.parseText(request.reader.text)
def s = new Sensor (sensorid: "SID", sensorname: "name", sensordescription: "description")
.addToReadings(reading: "blah")
.save()
render ([ok: false] as JSON)
}
}
sensor.groovy
package grailshelloworld
class Sensor {
String sensorid
String sensorname
String sensordescription
static hasMany = [readings: Readings]
static constraints = {
sensorid blank:false, nullable: false
sensorname blank:false, nullable: false
}
}
Readings.grooovy
package grailshelloworld
import java.util.Formatter.DateTime;
class Readings {
String reading
static belongsTo = [sensor: Sensor]
}
Current error: argument type mismatch...
<dt>Class</dt><dd>java.lang.IllegalArgumentException</dd><dt>Message</dt><dd>argument type mismatch</dd></dl><h2>Around line 15 of <span class="filename">grails-app/controllers/grailshelloworld/ReadingsController.groovy</span></h2>
<pre class="snippet"><code class="line"><span class="lineNumber">12:</span> def slurper = new JsonSlurper()</code><code class="line"><span class="lineNumber">13:</span> def result = slurper.parseText(request.reader.text)</code><code class="line"><span class="lineNumber">14:</span></code><code class="line error"><span class="lineNumber">15:</span> def s = new Sensor (sensorid: "SID", sensorname: "name", sensordescription: "description")</code><code class="line"><span class="lineNumber">16:</span> .addToReadings(reading: "blah")</code><code class="line"><span class="lineNumber">17:</span> .save()</code><code class="line"><span class="lineNumber">18:</span></code></pre><h2>Around line 195 of <span class="filename">PageFragmentCachingFilter.java</span></h2>

Have you tried it by explicitly creating a new Readings?
def s = new Sensor (sensorid: "SID", sensorname: "name", sensordescription: "description")
.addToReadings(new Readings(reading: 'blah'))
.save()
The error is saying "around line 15" which is the start to the def s = ... statement.
I know the docs say it can be done the way you are attempting - but it's worth a try.

Related

How to use Spatial Type in Grails4 / GORM7?

How can I model spatial type property in GORM7 and how can I query these spatial properties like other ordinary types such as String/Date/Number etc.
The Hibernate support spatial types by introducing specific dialect and classes such as "MySQLSpatialDialect" and "com.vividsolutions.jts.geom.Point".
I found some plugins for grails-v1/v2, but it is not for grails4.
If I have to write a plugin to do this, how can I start it?
It's all inside the box, but you need to configure it a bit!
Create your definition table. Note, that if you auto-create, mysql might not choose correct datatype for the location.
create table book (id bigint auto_increment primary key, version bigint, title varchar(255), location point)
In your application.yml, you need to add the correct dialect:
dataSource:
dialect: org.hibernate.spatial.dialect.mysql.MySQLSpatialDialect
...etc
To your build.gradle (note, vividsolutions changed name to locationtech):
compile "org.locationtech.jts:jts-core:1.18.1"
runtime "org.hibernate:hibernate-spatial:5.5.2.Final"
runtime 'mysql:mysql-connector-java:8.0.18'
Create a domain class with a geometry field:
import org.locationtech.jts.geom.Point
class Book {
String title
Point location
}
Create service for your books:
import grails.gorm.services.Service
import org.locationtech.jts.geom.Point
#Service(Book)
interface BookService {
Book save(String title, Point location)
}
Create some dummy data:
import org.locationtech.jts.io.WKTReader
import org.locationtech.jts.geom.Point
Book book = bookService.save('Test', (Point)new WKTReader().read("POINT (0 1)"))
To test your book location against some polygon:
import org.locationtech.jts.io.WKTReader
class Controller {
def sessionFactory
#ReadOnly
def index() {
def q = new WKTReader()
def filter = q.read('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))')
def session2 = sessionFactory.currentSession
def queryParams = [:]
queryParams.loc = filter
List<Book> results = new ArrayList<>()
def query =
"""
select b from Book b
where st_within(b.location, :loc) = true
"""
results = Book.executeQuery(query, queryParams)
println(results)
}
}

GORM where query on an embedded object

I have domain classes A and B as follows:
class A {
String prop1
String prop2
B prop3
static embedded = ['prop3']
}
class B {
String prop4
String prop5
}
When I want to query like this:
def q = A.where { prop3.prop4 == 'bla' }
def list = q.list()
I get the following exception:
Cannot get property 'javaClass' on null object. Stacktrace follows:
on the "def q = A.where ..." line.
Any clue what's the problem? I've checked this:
http://grails.1312388.n4.nabble.com/GORM-embedded-object-issue-td1379137.html
but how to "just call them directly" is not quite clear to me. Any other way of querying the embedded objects in GORM?
I finally gave up on the where query and went with the DetachedCriteria approach. Gives me the same flexibility as the where queries, but works with embedded domain objects:
def criteria = new DetachedCriteria(A).build {
eq 'prop1', 'bla2'
}
criteria = criteria.build {
eq 'prop3.prop4', 'bla'
}
def list = criteria.list()
What do you get if you do (assuming B is in src/groovy)
def q = A.where { prop3 == new B(prop4: 'bla') }
def list = q.list()
Embedded components are persisted inside the main domain class (owner) itself. It can be accessed directly using any dynamic finder as you do directly on a domain object.
The above can also be represented in dynamic finders as:
A.findAllByProp3(new B(prop4: 'bla'))

closure over each object of class

i am trying to call a closure over each object of a class, and then displaying the objects which returned by the closure.
the closure is:
def activitiesPlanned={
cal.set(this.plannedStartDate)
def planDateMonth=cal.get(Calendar.MONTH)
def planDateYear=cal.get(Calendar.YEAR)
}
the call i made is:
def getActivitiesPlanned(int month,int year){
countActivitiesPlanned=ProgressData.each{it.activitiesPlanned.findAllWhere(planDateMonth:month,planDateYear:year).count()}
println countActivitiesPlanned
}
Domain Class //EDIT
package main
class ProgressData {
//String milestoneName
String taskId //Added later
String taskDescription
String taskCategory
Integer plannedHours
Integer actualHours
Date plannedStartDate
Date actualStartDate
Date plannedEndDate
Date actualEndDate
Integer stepsCreated=0
Integer stepsExecuted=0
String status //Originally Completed
String assignedTo
//String unplanned
String accepted //Added later
def ProgressData(){}
static constraints = {
//milestoneName(blank:false)
taskDescription(blank:false)
taskCategory(blank:false)
plannedHours(blank:false)
actualHours(blank:false)
id generator:"assigned",name:"taskId"
}
Calendar cal=Calendar.getInstance()
def activitiesPlanned={
cal.set(this.plannedStartDate)
def planDateMonth=cal.get(Calendar.MONTH)
def planDateYear=cal.get(Calendar.YEAR)
}
static hasMany=[defects:DefectData]
}
I am getting: "No such property: activitiesPlanned for class: main.ProgressData Possible solutions: activitiesPlanned". what issue may be there?
I think you don't want .each{} rather .sum{}
Example:
def listThing = [1,2,3]
def sumOfList = listThing.sum{it}
assert sumOfList == 6
Also ProgressData is a Domain Class so you can't use .each{} on it. You have to do ProgressData.list().each{ and by that I really mean ProgressData.list().sum{ for what I think you're trying to do.
Also, that activitiesPlanned closure doesn't return any activities...

Grails/GORM saves in the wrong order

I am using Grails 1.3.6 and I am having problems getting saves to cascade properly. The problem is specifically with classes that have two parent classes. A simplified version of what I am attempting is this:
class Location {
String city
static hasMany = [authors: Author, publishers: Publisher]
}
class Author {
String name
static belongsTo = [location: Location]
static hasMany = [books: Book]
}
class Publisher {
String name
static belongsTo = [location: Location]
static hasMany = [books: Book]
}
class Book {
String title
static belongsTo = [author: Author, publisher: Publisher]
}
class Srv1Service {
static transactional = true
def loadData() {
def l1 = new Location(city: "London")
def a1 = new Author(name: "Graham Greene")
l1.addToAuthors(a1)
def p1 = new Publisher(name: "Some Press")
l1.addToPublishers(p1)
def b1 = new Book(title: "The Comedians")
a1.addToBooks(b1)
p1.addToBooks(b1)
l1.save()
}
}
If I run the above loadData, the Book instance is saved before the Publisher instance, resulting in the error "not-null property references a null or transient value: adhoc.Book.publisher".
I have tried various different ways of defining the relationships with little success. I have tried interim saves, and this does work, but I can see that parent tables are updated as I save the child data - ie Location, Author and Publisher all get updated to version 1. (And also I would like to keep the code as simple as I can.) I would like to avoid linking tables.
Any advice is gratefully received!
Okay, so the key here is that saves are cascaded from parent to children. You have a problem when it comes to Book because Book is the child to both Publisher and Author. GORM tries to save Location, Location tries to save Author, Author tries to save Book BUT the save fails because Book has a transient Publisher.
Try adding an intermediate save right before you create your Book:
def loadData() {
def l1 = new Location(city: "London")
def a1 = new Author(name: "Graham Greene")
l1.addToAuthors(a1)
def p1 = new Publisher(name: "Some Press")
l1.addToPublishers(p1)
l1.save() // add this save
def b1 = new Book(title: "The Comedians")
a1.addToBooks(b1)
p1.addToBooks(b1)
l1.save()
}
I created a local Grails project with your domain classes adding in this save. The cascading is working as you would expect.

where is bidirectional ? grails one-to-one, testing bidirectional (continue)

class Book {
String title
Date releaseDate
String ISBN
static belongsTo = [person:Person] // it makes relationship bi-directional regarding the grails-docs
}
class Person {
Book book; // it will create person.book_id
String name
Integer age
Date lastVisit
static constraints = {
book unique: true // "one-to-one". Without that = "Many-to-one".
}
}
There is a test which test if it is real bidirectional or not. As i understand it.
public void testBidirectional() {
def person = new Person(name:"person_c1", age: 99, lastVisit: new Date())
def book = new Book(
title:"somebook_c1",
ISBN: "somebook_c1",
releaseDate: new Date()
)
person.setBook (book)
assertNotNull(person.save())
def bookId = person.getBook().id
Book thatBook = Book.get(bookId)
assertNotNull(thatBook.person) // NULL !!!
}
So, i save a person with a book, and then i got that book from db by id. Then from that book i try to get back the person which book should refer to (because it should be bidirectional, right?). Eventually i got null instead of an instance of the person.
The questing is: how to make that test working?
i have found the solution how to get it working, but still can not understand why it does not work without 'refresh', see below:
public void testBidirectional() {
def person = new Person(name:"person_c1", age: 99, lastVisit: new Date())
def book = new Book(
title:"somebook_c1",
ISBN: "somebook_c1",
releaseDate: new Date()
)
person.setBook (book)
def p = person.save()
assertNotNull p
person.refresh() //load the object again from the database so all the changes made to object will be reverted
//person = Person.get(p.id) // BUT this also gets the object from db ...?
def bookId = person.getBook().id
assertNotNull bookId
def thatBook = Book.get(bookId)
assertNotNull(thatBook.person)
}
So, here as you can see i use 'refresh' to get it working, but why it does not work without 'refresh' but with the following line after 'refresh' - this one:
person = Person.get(p.id) // BUT this also gets the object from db ...?
If i just want to get object from database by id, then it would be without bidirectional?
Your problem is probably caused by the way that Hibernate works. Grails used Hibernate under the hood.
Even when you call "save", the object person may (and usually) not saved in database. That's because Hibernate is programmed to optimize the query, so it often waits to perform all query at then end of the Hibernate session.
That means if you don't call "refresh", the book-person relation (person.setBook) is still in memory, but not saved in database. Hence you can't get the book.person from book.
To enforce the save, you can use "refresh" like the previous answer, or use flush:true.
I still not try, but it's very likely that you will produce desired results with:
person.save(flush:true)

Resources