I am using Grails version 2.3.3, and groovy version 2.1.8.
I am using an online tutorial to help learn Grails web dev, and I have created a domain class with the following constraints
package racetrack
class Race {
static constraints = {
name(blank:false, maxSize:50)
city(blank:false)
state(inList:["GA", "NC", "SC", "VA"])
startDate()
distance(min: 0.0)
cost(min: new BigDecimal(0.0), max: new BigDecimal(100.0))
maxRunners(min:0, max:100000)
}
String name
Date startDate
String city
String state
BigDecimal distance
BigDecimal cost
Integer maxRunners
}
I'm using scaffolding so I have full CRUD functionality. The problem is, when I go to create a new Race, the app allows me to input values like "-1" in both the distance and cost fields, and values like "200" for the cost field. I noticed that the Integer field maxRunners was working correctly, as it displays a warning message if I try to put in -1 maxRunners. I changed the cost field to be of type Integer, and then the constraints started working.
Why is this happening? I copy and pasted the code from the tutorial into my text file and the constraints to not work for BigDecimal type fields.
According to the docs for the min and max constraints, the parameter must be
a class that implements java.lang.Comparable. The type of the value must be the same as the property.
so I can't see any reason why your definition shouldn't work. As a workaround, you could try replacing this
cost(min: new BigDecimal(0.0), max: new BigDecimal(100.0))
with
cost(range: 0..100)
and you could replace this constraint:
distance(min: 0.0)
with a custom validator, e.g.
distance validator: {
it >= 0.0
}
I've just noticed the following oddity which appears before the 2 problematic constraints
startDate()
Perhaps replacing it with either:
startDate(nullable: true)
or
startDate(nullable: false)
might fix the problem?
Doesn't Groovy use java.math.BigDecimal by default? Could you change new BigDecimal(0.0) to simply 0.0 and new BigDecimal(100.00) to 100.00 like this:
cost(min: 0.0, max: 100.0)
Here is the documentation that states:
To support the 'least surprising' approach, groovy literals with decimal points are instantiated as java.math.BigDecimal types rather than binary floating point types (Float, Double).
Also, you could try forcing it by using the G suffix like this:
cost(min: 0.0G, max: 100.0G)
Related
Used tools and their versions:
I am using:
spring boot 2.2.6
hibernate/hibernate-spatial 5.3.10 with dialect set to: org.hibernate.spatial.dialect.mysql.MySQL56SpatialDialect
querydsl-spatial 4.2.1
com.vividsolutions.jts 1.13
jscience 4.3.1
Problem description:
I have an entity that represents medical-clinic:
import com.vividsolutions.jts.geom.Polygon;
#Entity
public class Clinic {
#Column(name = "range", columnDefinition = "Polygon")
private Polygon range;
}
The range is a circle calculated earlier based on the clinic's gps-location and radius. It represents the operating area for that clinic. That means that it treats only patients with their home address lying within that circle. Let's assume that above circle, is correct.
My goal (question):
I have a gps point with a patient location: 45.7602322 4.8444941. I would like to find all clinics that are able to treat that patient. That means, to find all the clinics that their range field contains 45.7602322 4.8444941.
My solution (partially correct (I think))
To get it done, I have created a simple "Predicate"/"BooleanExpression":
GeometryExpressions.asGeometry(QClinic.clinic.range)
.contains(Wkt.fromWkt("Point(45.7602322 4.8444941)"))
and it actualy works, because I can see proper sql query in console:
select (...) where
ST_Contains(clinic0_.range, ?)=1 limit ?
first binding param: POINT(45.7602322 4.8444941)
But I have two problems with that:
QClinic.clinic.range is marked as "warning" in intellij as: "Unchecked assignment: 'com.querydsl.spatial.jts.JTSPolygonPath' to 'com.querydsl.core.types.Expression<org.geolatte.geom.Geometry'". Yes, in QClinic range is com.querydsl.spatial.jts.JTSPolygonPath
Using debugger and intellij's "evaluate" on the above line (that creates the expression) i can see that there is an error message: "unknown operation with operator CONTAINS and args [clinic.range, POINT(45.7602322 4.8444941)]"
You can ignore the second warning. The spatial specific operations are simply not registered in the serializer used for the toString of the operation. They reside in their own module.
The first warning indicates that you're mixing up Geolatte and JTS expressions. From your mapping it seems you intend to use JTS. In that case you need to use com.querydsl.spatial.jts.JTSGeometryExpressions instead of com.querydsl.spatial.jts.GeometryExpressions in order to get rid of that warning.
My string columns are defaulting to 20 characters. All the documentation suggests the default is 255. How do I set that without changing each individual column. I am using Grails 3.3.8 and Postgresql 9.3.
class DbConnection {
String name
String platform
creates 20 character columns
If I add a mapping:
static mapping = {
name sqlType: 'varchar(255)'
platform sqlType: 'varchar(255)'
url sqlType: 'varchar(255)'
}
I get the proper 255 characters, however, grails fails the string on validation:
Field error in object 'dop_etc.DbConnection' on field 'url': rejected value [localhost:5432/rbc48_fantasy]; codes [dop_etc.DbConnection.url.size.error.dop_etc.DbConnection.url,dop_etc.DbConnection.url.size.error.url,dop_etc.DbConnection.url.size.error.java.lang.String...
There seems to be a default size set somewhere and I can't seem to find it. Thanks for your responses.
Apologies, found the problem in some pasted code (in application.groovy)
grails.gorm.default.constraints = {
'*'(nullable: true, size: 1..255)
}
Here's the helpful article:
Overriding default `maxSize` in domain class
I have a domain class with a float field:
class DvQuantity {
float magnitude
String units
}
When I have this:
new DvQuantity( magnitude: '11.0', units: 'm' ).save()
In the database I see: magnitude = 110.0 instead of 11.0
Is this a Grails bug? Is there any workaround?
It is not a bug. As stated in the comment, parsing of numbers depends on your locale. See example below:
import java.text.NumberFormat
String a = '11,0'
assert NumberFormat.getInstance(Locale.US).parse(a).floatValue() == 110
assert NumberFormat.getInstance(Locale.FRANCE).parse(a).floatValue() == 11.0
There are many different ways to change your default number format - for example PropertyEditorRegistrar or ValueConverter. You can also enforce format on the view using validators.
Here Grails databinding with decimal delimiter is answer with example code how to do that for Grails <2.3 and >=2.4
class Donation {
BigDecimal amount
static constraints = {
amount min: BigDecimal.ZERO
}
static mapping = {
}
}
After the addition, the domain looked like below:
class Donation {
BigDecimal amount
static constraints = {
amount min: BigDecimal.ZERO
}
static mapping = {
amount scale: 4
}
}
After making the change, i ran dbm-gorm-diff in the grails console but it printed no additional changelog lines. I was wondering if making mapping change will not produce new changelog lines. But looking at the data type of "amount" in mysql database, it showed decimal(19,2). I thought making scale 4 will change the datatype to decimal(19, 4). I appreciate any help in this dilemma. Thanks!
While db-migration can detect changes in your domains and can generate the desired changelog to update your database schema. It is not smart enough to locate correct changes all the time. Specifically while you are renaming a table/column or changing the datatype of a column. In these kind of scenarios you need to make manula migrations.
Make a manual changelog to update schema using db-migration:
databaseChangeLog = {
changeSet(author: "sandeep (manual)", id: "20150901124635-01") {
modifyDataType(columnName: "amount", newDataType: "decimal(19,4)", tableName: "donation")
}
}
So always, after generating the changelog, verify whether it contains the correct changes or not.
I have a grails domain class Character
class Character {
String name
int level
boolean alive
Player player
static constraints = {
name(blank:false, unique:true)
level(min:1)
player(nullable:false)
}
}
I want to query for a character with a specified player, where the value of alive is "true". I tried using the following, but it
Character.findByPlayerAndAliveEqual(p, true)
But it generates an exception
No signature of method: static java.lang.Character.findByPlayerAndAliveEqual() is applicable for argument types: (com.thestreetsgame.security.Player, java.lang.Boolean) values: [com.thestreetsgame.security.Player : 1, true]
I've also tried findByPlayerAndAlive, with the same result. How can I make this gorm query work?
Oops, the important part of the exception just jumped out at me.
java.lang.Character
I was trying to do a lookup on the core java class instead of my domain class. Need to always use the fully qualified name, or change the name of the class.
For the time being I've fully qualified the reference, and it is working.