class User {
String login
String password
String email
Integer age
static constraints = {
...
}
}
13 Validation and Constraints
Version: 2023.3.0-SNAPSHOT
Table of Contents
13 Validation and Constraints
Constraints are how you define validation when using GORM entities.
13.1 Applying Constraints
Within a domain class constraints are defined with the constraints property that is assigned a code block:
You then use method calls that match the property name for which the constraint applies in combination with named parameters to specify constraints:
class User {
...
static constraints = {
login size: 5..15, blank: false, unique: true
password size: 5..15, blank: false
email email: true, blank: false
age min: 18
}
}
In this example we’ve declared that the login
property must be between 5 and 15 characters long, it cannot be blank and must be unique. We’ve also applied other constraints to the password
, email
and age
properties.
By default, all domain class properties are not nullable (i.e. they have an implicit nullable: false constraint).
|
Note that constraints are only evaluated once which may be relevant for a constraint that relies on a value like an instance of java.util.Date
.
class User {
...
static constraints = {
// this Date object is created when the constraints are evaluated, not
// each time an instance of the User class is validated.
birthDate max: new Date()
}
}
13.2 Referencing Instances in Constraints
It’s very easy to attempt to reference instance variables from the static constraints block, but this isn’t legal in Groovy (or Java). If you do so, you will get a MissingPropertyException
for your trouble. For example, you may try the following:
class Response {
Survey survey
Answer answer
static constraints = {
survey blank: false
answer blank: false, inList: survey.answers
}
}
See how the inList
constraint references the instance property survey
? That won’t work. Instead, use a custom validator
constraint:
class Response {
...
static constraints = {
survey blank: false
answer blank: false, validator: { val, Response obj -> val in obj.survey.answers }
}
}
In this example, the obj
argument to the custom validator is the domain instance that is being validated, so we can access its survey
property and return a boolean to indicate whether the new value for the answer
property, val
, is valid.
13.3 Cascade constraints validation
If GORM entity references some other entities, then during its constraints evaluation (validation) the constraints of the referenced entity could be
evaluated also, if needed. There is a special parameter cascadeValidate
in the entity mappings section, which manage the way of this cascaded validation happens.
class Author {
Publisher publisher
static mapping = {
publisher(cascadeValidate: "dirty")
}
}
class Publisher {
String name
static constraints = {
name blank: false
}
}
The following table presents all options, which can be used:
Option | Description |
---|---|
none |
Will not do any cascade validation at all for the association. |
default |
The DEFAULT option. GORM performs cascade validation in some cases. |
dirty |
Only cascade validation if the referenced object is dirty via the |
owned |
Only cascade validation if the entity owns the referenced object. |
It is possible to set the global option for the cascadeValidate
:
grails.gorm.default.mapping = {
'*'(cascadeValidate: 'dirty')
}
13.4 Constraints Reference
The following table summarizes the available constraints with a brief example:
Constraint | Description | Example |
---|---|---|
blank |
Validates that a String value is not blank |
|
creditCard |
Validates that a String value is a valid credit card number |
|
Validates that a String value is a valid email address. |
|
|
inList |
Validates that a value is within a range or collection of constrained values. |
|
matches |
Validates that a String value matches a given regular expression. |
|
max |
Validates that a value does not exceed the given maximum value. |
|
maxSize |
Validates that a value’s size does not exceed the given maximum value. |
|
min |
Validates that a value does not fall below the given minimum value. |
|
minSize |
Validates that a value’s size does not fall below the given minimum value. |
|
notEqual |
Validates that that a property is not equal to the specified value |
|
nullable |
Allows a property to be set to |
|
range |
Uses a Groovy range to ensure that a property’s value occurs within a specified range |
|
scale |
Set to the desired scale for floating point numbers (i.e. the number of digits to the right of the decimal point). |
|
size |
Uses a Groovy range to restrict the size of a collection or number or the length of a String. |
|
unique |
Constrains a property as unique at the database level |
|
url |
Validates that a String value is a valid URL. |
|
validator |
Adds custom validation to a field. |
See documentation |
13.5 Constraints and Database Mapping
Although constraints are primarily for validation, it is important to understand that constraints can affect the way in which the database schema is generated.
Where feasible, GORM uses a domain class’s constraints to influence the database columns generated for the corresponding domain class properties.
Consider the following example. Suppose we have a domain model with the following properties:
String name
String description
By default, in MySQL, GORM would define these columns as
Column | Data Type |
---|---|
name |
varchar(255) |
description |
varchar(255) |
But perhaps the business rules for this domain class state that a description can be up to 1000 characters in length. If that were the case, we would likely define the column as follows if we were creating the table with an SQL script.
Column | Data Type |
---|---|
description |
TEXT |
Chances are we would also want to have some application-based validation to make sure we don’t exceed that 1000 character limit before we persist any records. In GORM, we achieve this validation with constraints. We would add the following constraint declaration to the domain class.
static constraints = {
description maxSize: 1000
}
This constraint would provide both the application-based validation we want and it would also cause the schema to be generated as shown above. Below is a description of the other constraints that influence schema generation.
Constraints Affecting String Properties
-
inList
-
maxSize
-
size
If either the maxSize
or the size
constraint is defined, Grails sets the maximum column length based on the constraint value.
In general, it’s not advisable to use both constraints on the same domain class property. However, if both the maxSize
constraint and the size
constraint are defined, then GORM sets the column length to the minimum of the maxSize
constraint and the upper bound of the size constraint. (GORM uses the minimum of the two, because any length that exceeds that minimum will result in a validation error.)
If the inList
constraint is defined (and the maxSize
and the size
constraints are not defined), then GORM sets the maximum column length based on the length of the longest string in the list of valid values. For example, given a list including values "Java", "Groovy", and "C++", GORM would set the column length to 6 (i.e., the number of characters in the string "Groovy").
Constraints Affecting Numeric Properties
-
min
-
max
-
range
If the max
, min
, or range
constraint is defined, GORM attempts to set the column precision based on the constraint value. (The success of this attempted influence is largely dependent on how Hibernate interacts with the underlying DBMS.)
In general, it’s not advisable to combine the pair min
/max
and range
constraints together on the same domain class property. However, if both of these constraints is defined, then GORM uses the minimum precision value from the constraints. (GORM uses the minimum of the two, because any length that exceeds that minimum precision will result in a validation error.)
-
scale
If the scale constraint is defined, then GORM attempts to set the column scale based on the constraint value. This rule only applies to floating point numbers (i.e., java.lang.Float
, java.Lang.Double
, java.lang.BigDecimal
, or subclasses of java.lang.BigDecimal
). The success of this attempted influence is largely dependent on how Hibernate interacts with the underlying DBMS.
The constraints define the minimum/maximum numeric values, and GORM derives the maximum number of digits for use in the precision. Keep in mind that specifying only one of min
/max
constraints will not affect schema generation (since there could be large negative value of property with max:100, for example), unless the specified constraint value requires more digits than default Hibernate column precision is (19 at the moment). For example:
someFloatValue max: 1000000, scale: 3
would yield:
someFloatValue DECIMAL(19, 3) // precision is default
but
someFloatValue max: 12345678901234567890, scale: 5
would yield:
someFloatValue DECIMAL(25, 5) // precision = digits in max + scale
and
someFloatValue max: 100, min: -100000
would yield:
someFloatValue DECIMAL(8, 2) // precision = digits in min + default scale