Although constraints are covered in the
Validation section, it is important to mention them here as some of the constraints can affect the way in which the database schema is generated.
Where feasible, Grails 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 property.
String name
String description
By default, in MySQL, Grails would define these columns as...
column name | data type
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'd likely define the column as follows
if we were creating the table via an SQL script.
column name | data type
description | TEXT
Chances are we'd 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 Grails, we achieve this validation via
constraints. We'd 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
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 Grails sets the column length to the minimum of the
maxSize
constraint and the upper bound of the size constraint. (Grails 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 Grails 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++", Grails would set the column length to 6 (i.e., the number of characters in the string "Groovy").
Constraints Affecting Numeric Properties
If the
max
constraint, the
min
constraint, or the
range
constraint is defined, Grails 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 Grails uses the minimum precision value from the constraints. (Grails uses the minimum of the two, because any length that exceeds that minimum precision will result in a validation error.)
If the scale constraint is defined, then Grails 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 Grails 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 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