I am trying to use beforeInsert in my user domain class.
class User {
String reEnterPassword
static constraints = {
password(blank: false, nullable: false, size:5..50, validator: {password, obj ->
def reEnterPassword = obj.properties['reEnterPassword']
if(reEnterPassword == null) return true
reEnterPassword == password ? true : ['invalid.matchingpasswords']
})
reEnterPassword(bindable:true, blank: false);
}
def beforeInsert = {
password = password.encodeAsSHA()
}
String toString(){
name
}
static transients = ['reEnterPassword']
}
in my controller i have save method ( generated)
def save() {
def userInstance = new User(params)
if (!`userInstance.save(flush: true)`) {
render(view: "create", model: [userInstance: userInstance])
return
}
This is throwing exception
Grails runtime exception, org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs), when domain objects save method encounters a SQL Exception
I read in the documentation for auto timestamping that
Do not attempt to flush the session within an event (such as with obj.save(flush:true)). Since events are fired during flushing this will cause a StackOverflowError.
In this case how to save my userInstance.save(flush: true) I tried to remove flush:true but still i am getting same error. if i remove flus:true..then when i need to call. When hibenate will flush all these records.
I tried the solution defined this JIRA ticket
Please help me out. Thank you
Can it be that you have other validation errors?
If you put your code in the beforeValidate method it will work:
def beforeValidate = {
password = password.encodeAsSHA()
}
I guess I'm too late to help you, but I hope it helps others with the same issue.
Greetings, Urs
Change your
def beforeInsert = {
password = password.encodeAsSHA()
}
to
def beforeInsert() {
password = password.encodeAsSHA()
}
and that should do the trick
I believe if the beforeInsert method return false then you get the "null id in entry" exception. Perhaps this is treated as an indication that validation has failed.
e.g. the following will cause the exception
def beforeInsert() {
flag = false
}
however the following should work OK
def beforeInsert() {
flag = false
return true
}
Related
I try to validate a nested domain class instance on a command object.
Having the following command object
package demo
import grails.databinding.BindingFormat
class SaveEventCommand {
#BindingFormat('yyyy-MM-dd')
Date date
Refreshment refreshment
static constraints = {
date validator: { date -> date > new Date() + 3}
refreshment nullable: true
}
}
And having the following domain class with its own constraints
package demo
class Refreshment {
String food
String drink
Integer quantity
static constraints = {
food inList: ['food1', 'food2', 'food3']
drink nullable: true, inList: ['drink1', 'drink2', 'drink3']
quantity: min: 1
}
}
I need when refreshment is not nullable the command object validates the date property and check the corresponding restrictions in refreshment instance
For now try with this code in the controller:
def save(SaveEventCommand command) {
if (command.hasErrors() || !command.refreshment.validate()) {
respond ([errors: command.errors], view: 'create')
return
}
// Store logic goes here
}
Here through !command.refreshment.validate() I try to validate the refresh instance but I get the result that there are no errors, even when passing data that is not correct.
Thank you any guide and thank you for your time
I typically just include some code that will use a custom validator to kick off validation for any property that is composed of another command object. For example:
thePropertyInQuestion(nullable: true, validator: {val, obj, err ->
if (val == null) return
if (!val.validate()) {
val.errors.allErrors.each { e ->
err.rejectValue(
"thePropertyInQuestion.${e.arguments[0]}",
"${e.objectName}.${e.arguments[0]}.${e.code}",
e.arguments,
"${e.objectName}.${e.arguments[0]}.${e.code}"
)
}
}
})
This way it's pretty clear that I want validation to occur. Plus it moves all the errors up into the root errors collection which makes things super easy for me.
Two things I could think of:
Implement grails.validation.Validateable on your command object
What happens when you provide an invalid date? Can you see errors while validating?
In Grails, it is said that by default, nullable is already set to false. But when I try to run student.validate() and not set studentId to nullable:false; validate cannot catch the error hence I get a MySQLIntegrityConstraintViolationException.
Is my observation correct? or do you think I've messed up some code that's why validate cannot catch the error?
And if my observation is right. what is the reason behind it?
#Validateable
class Student {
Long studentId
.
.
static constraints = {
studentId nullable:false
.
.
}
}
def saveCreate(Student student){
if (!student.validate()) {
List errorList = buildErrorMessages(ErrorBean.getErrorBeans(student.errors), []);
render([isSuccess: false, errors: errorList] as JSON);
return;
}
student.save(flush: true);
def Map studentInformation = [id: student.id, name: student.name];
render([isSuccess: true, student: studentInformation] as JSON);
}
If i add this in static constraints
studentId nullable: false
when I run student.validate, validate can catch the error. If I didnt put it, validate will return true and will try to save the data.
I've seen that others have had this problem, but I have yet to find a solution that works in my case. In my update method of my domain controller, I am trying to implement optimistic locking by checking version and using rejectValue(); however, I am clearly doing something wrong. I have verified that rejectValue() is being called, but it doesn't appear to work.
Also, the domain instance with bad data saves anyway. Any advice or help is appreciated. Here is my update method with the problem:
def update(Cohort cohortInstance) {
if (cohortInstance == null) {
notFound()
return
}
cohortInstance = Cohort.get(params.id)
if (cohortInstance == null) {
notFound()
return
}
if (params.version) {
def version = params.version.toLong()
if (cohortInstance.version > version) {
cohortInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'cohort.label', default: 'Cohort')] as Object[],
"Another user has updated this Cohort while you were editing")
render(view: "edit", model: [cohortInstance: cohortInstance])
return
}
}
// Bind browser data to this cohort Instance now that we've passed the concurrency check
cohortInstance.properties = params
if (cohortInstance.hasErrors()) {
respond cohortInstance.errors, view:'edit'
return
}
cohortInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'Cohort.label', default: 'Cohort'), cohortInstance.proposedCode])
redirect cohortInstance
}
'*'{ respond cohortInstance, [status: OK] }
}
}
In your update method definition (def update(Cohort cohortInstance) {}), you are initializing the instance of Cohort which is a new feature in grails 2.3.x, where grails automatically initializes the instance & do parameter binding for you .
Then again on line 4, you are getting the Cohort instance by using Cohort.get(params.id). So now you have two options: First is that, you can use read method instead of get and remove the auto initialization from the action signature or second is that, you can remove this line (cohortInstance = Cohort.get(params.id)) and add a Transactional annotation at update action like:
import grails.transaction.Transactional
#Transactional(readOnly = true)
def update(Cohort cohortInstance) {
...
}
Any of these work.
I have a domain object with following:
class Color {
String name
String fileLocation
static constraints = {
name (nullable: false, blank: false)
}
}
In my controller I'm doing the following:
def save() {
def colorInstance = new Color(params)
if (colorInstance.save(flush: true)) {
def file = request.getFile("myfile")
if (!file.empty && uploadService.isFileAllowed(file)) {
uploadService.uploadFile(file, file.originalName, "folderName")
}
}
else {
render (view: "create", model: [coorInstance: colorInstance])
}
}
This all works fine however, I'm not sure how to throw an error when the uploaded file isn't what is allowed. i.e. uploadService.isFileAllowed(file) returns false ??
How can I throw an error back to the user saying
Uploaded file isn't allowed
when uploadService.isFileAllowed(file) returns false ?
Note:
The isFileAllowed method is reading first few bytes of a file to determine what type of file it is.
What if you save an error message to flash memory and then render it on the page if it exists? See this post for help.
if (!file.empty && uploadService.isFileAllowed(file)) {
uploadService.uploadFile(file, file.originalName, "folderName")
} else {
flash.error = "Uploaded file isn't allowed"
}
apply this login in your controller
String fileName = "something.ext";
int a = fileName.lastIndexOf(".");
String extName = fileName.substring(a);
System.out.println(fileName.substring(a));
ArrayList<String> extList = new ArrayList<String>();
extList.add("jpg");
extList.add("jpeg");
extList.add("png");
if(extList.contains(extName))
{
System.out.println("proceed");
}
else{
System.out.println("throw exception");
}
So if isFileAllowed returns false or the file is empty, it will add an error to the colorInstance to the fileLocation property. It will only upload the file if the colorInstance validates successfully (to prevent files uploaded for unsaved objects).
As a side note, I prefer saving files in tables partly for this reason. It makes validation much less clunky and its impossible to have a disconnect between your objects and the files. - Just my 2c.
def save() {
def colorInstance = new Color(params)
def file = request.getFile("myfile")
if (!file.empty && uploadService.isFileAllowed(file)) {
if (colorInstance.validate()) {
uploadService.uploadFile(file, file.originalName, "folderName")
}
}
else {
colorInstance.errors.rejectValue('fileLocation','error.message.code.here')
}
if (colorInstance.save(flush: true)) {
//do whatever here
}
else {
render (view: "create", model: [coorInstance: colorInstance])
}
}
I have a Spring Security User class which has a unique constraint for username and email. In a Command class I imported all constraints from this class with "importFrom User". All constraints work as expected EXCEPT the unique ones.
However when saving the User the unique constraints get validated and errors are shown. But it would be nice if they get validated BEFORE saving like all other constraints.
UPDATE
I added this to the controller:
user.errors.fieldErrors.each {
command.errors.rejectValue(it.getField(), it.getCode())
}
Seems like a dirty workaround, but it works.
Good question #Chris, and your solution is best since the goal of sharing constraints between domain classes and command objects is to avoid duplicating validation logic.
I'll just add that to avoid duplicating field errors and to handle nested field paths in domain objects, something like the following might be necessary.
def save(EntityCreateCommand cmd) {
def entity = new Entity(cmd.properties)
def someAssociation = new Something(cmd.properties)
entity.someAssociation = someAssociation
entity.validate()
entity.errors.fieldErrors.each {
def fieldName = it.field.split("\\.").last()
def flattenedCodes = cmd.errors.getFieldErrors(fieldName).codes.flatten()
if(cmd.hasProperty(fieldName) && (!flattenedCodes.contains(it.code))) {
cmd.errors.rejectValue(fieldName,
"entityCreateCommand.${fieldName}.${it.code}")
}
}
if(cmd.errors.hasErrors()) {
error handling stuff...
} else {
business stuff...
}
}
I had problems with unique constraint before, so I made a custom validator in my command object to test and see if it's unique:
Command Object:
class wateverCommand{
....
String username
static constraints = {
username validator:{value, command ->
if(value){
if(User.findByUsername(value){
return 'wateverCommand.username.unique'
}
}
}
}
}
within your messages.properties add a custom error message:
wateverCommand.username.unique The username is taken, please pick a new username
I agree the unique constraint doesn't always seem to import properly. Since I like to avoid clutter in the constraint body I like the one liner approach:
validator: {value, command -> (User.findByUsername(value) ? false : true ) }
Then in your message.properties it would be:
accountCommand.username.validator.error=That username already exists