How to catch duplicate key exceptions in Grails . when trying to save existing integer for a unique column constraint, the error is generating while saving/updating a record .
Also used
try{
object.save(flush:true)
}
catch(org.springframework.dao.DataIntegrityViolationException e){
println e.message
}
catch(org.hibernate.exception.ConstraintViolationException ex){
println e.message
}
catch(Exception e){
println e.message
}
But unable to catch this issue .
23:41:13,265 ERROR [JDBCExceptionReporter:101] Duplicate entry '1' for
key 2 23:41:13,281 ERROR [AbstractFlushingEventListener:324]
Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not
execute JDBC batch update at
org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
at
org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at
org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at
org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at
org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at
org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at
org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
Could you please share the solution to solve this .
You're trying to save a record with an id that already exists.
If id is auto-generated, don't set it manually
If id is not auto-generated, set it to something else, for example max(id) + 1
surely no Exception should be thrown for constraint violation, but rather object.save() should return null? i.e.
if(object.save() == null) {
// save failed
} else {
// save succeeded
}
If you defined the uniqueness through a Grails constraint, you have to look for a ValidationException. This is thrown when object.validate() fails; Validation is done before any object.save().
try {
object.save(failOnError: true)
}
catch(ValidationException ve) {
// Do something ...
}
But remember: Any constraint violation, for any member variable can cause a ValidationException ... so you have to distinguish yourself.
Edit:
This applies, if you use the Grails 1.2 failOnError feature ...
I am looking for the same problem so maybe not a complete answer but what you can do is to force validation and look in the errors, identify the case and place the actions you want:
if(instance.validate()) {
//everything ok
} else {
instance.errors.each {
//identify the case and place actions
}
}
Also note that error is: className.propertyName.unique
Possibly it should work:
import org.springframework.dao.DuplicateKeyException
try {
domainInstance.save(flush: true)
} catch(DuplicateKeyException e) {
// ...
}
Related
I'd like to know, if (and how) I could append some own error-messages to the domain-object after (or before) a validation.
My intention is, I have to check the uploaded file in a form for some attributes (image size etc.) and if something is wrong, I would like to add an error-message which is displayed in the usual grails ".hasErrors" loop.
(And I think I need to have the possibility to express errors in some cross-domain check failure...)
Thanks in advance,
Susanne.
You can add custom validation errors as described in the errors docs as follows:
class SampleController {
def save() {
def sampleObject = new SampleObject(params)
sampleObject.validate()
if(imageSizeIsTooBig(sampleObject)) {
sampleObject.errors.rejectValue(
'uploadedFile',
'sampleObject.uploadedFile.sizeTooBig'
)
}
private def imageSizeIsTooBig(SampleObject sampleObject) {
// calculation on sampleObject, if size is too big
}
Perhaps, you could even handle your case with a custom validator, so you can call validate() one time and be sure, that all constraints are fulfilled.
Here is a real example with a custom domain error:
def signup(User user) {
try {
//Check for some condition
if (!params.password.equals(params.passwordRepeat)) {
//Reject the value if condition is not fulfilled
user.errors.rejectValue(
'password',
'user.password.notEquals',
'Default message'
)
//Throw an exception to break action and rollback if you are in a service
throw new ValidationException('Default message', user.errors)
}
//Continue with your logic and save if everything is ok
userService.signup(user)
} catch (ValidationException e) {
//Render erros in the view
respond user.errors, view:'/signup'
return
}
}
In Grails i want to catch the exception when foreign key constraint appears, this is my code
try {
instance.delete(flush:true)
flash.message = message(code : "default.deleted.message", args : [instance])
flash.level = "info"
} catch(org.springframework.dao.DataIntegrityViolationException | Exception e) {
flash.message = message(code : "default.not.deleted.message", args : [instance])
flash.level = "danger"
}
The problem is when there is a foreign key constraint, it never enters the catch block.
Any idea what exception i should add ?
Thanks,
I am using grails 2.3.5 and following code works for me
try {
user.delete(flush: true)
render "OK"
} catch (org.springframework.dao.DataIntegrityViolationException e) {
render "DataIntegrityViolationException"
} catch (Throwable v) {
render "Throwable"
}
Try this..,.
I think it is because MySQLIntegrityConstraintViolationException is thrown on update or insert not delete
Exception thrown when an attempt to insert or update data results in violation of an
integrity constraint. Note that this is not purely a relational concept; unique primary
keys are required by most database types.
Try with com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException
EDIT
Which version of Grails are you using ?
I found this issue GRAILS-9357
Can anyone point out why I get an exception when the .results point in the code is executed?
-- note the code has been edited after the quested was answered as per Tatham Oddie's comment. ---
public User Create(User user)
{
try
{
// Check if user exists
if (this.Exists(user.EmailAddress))
{
throw new Exception("User already exists");
}
else
{
var q = this._context.Client().Cypher
.Create("(n:User {f}")
.WithParam("f", "Mike")
.Return(n => n.As<User>());
return q.Results.Single();
}
}
catch (Exception e)
{
throw e;
}
}
Please do not write code like this: "(n:User {FirstName: '" + user.FirstName + "'}". It is a major security risk in your application, and a performance constraint.
Follow the example at https://github.com/Readify/Neo4jClient/wiki/cypher-examples#create-a-user which uses a parameter syntax.
It will be secure.
It will be faster.
It will work.
Got it. Syntax error. Missing bracket.
Does anyone have a simple way of handling this exception when updating a record to one that already exists in the database?
Try this:
catch (UpdateException ex)
{
SqlException innerException = ex.InnerException as SqlException;
if (innerException != null && innerException.Number == ??????)
{
//Place you exception code handling here..
}
else
{
throw; //(bubble up)
}
}
This is a simple solution, but you may have issues in the future should the error number change which is unlikely).
I'm trying to implement exception handling for Optimistic lock type exceptions that are thrown by Hibernate but I've encountered a strange issue. It seems I'm unable to catch any Gorm exceptions.
For example I have this code in my service:
try {
User user = User.get(1);
Thread.sleep(10000);
user.viewedAt(new Date());
user.save(flush:true);
} catch (OptimisticLockingException ex) {
log.error("Optimistic lock exception");
} catch (StaleObjectStateException ex) {
log.error("Optimistic lock exception");
}
When I hit this block with two threads, it blows up and the exception propagates to Grails' standard exception handler. The catch blocks are never invoked even though the reported exception is StaleObjectStateException.
I've noticed that I can catch the exception if I let it propagate to the controller and catch it there, but it seems I can't implement exception handling in the service which is weird.
What am I missing?
I got to the bottom of this and I'm posting it in case anyone else runs into this. The issue occurred because the try/catch block was in a transactional service. Although grails reported that the exception was thrown during the save() call, in reality it was called AFTER the entire method, when the transaction was committed.
So it seems that:
flush: true has no effect on transactional services
It's not possible to catch GORM related exceptions in transactional services, at least not without some work
I finally worked around this by manually managing the transaction myself i.e.
try {
User.withNewTransaction {
User user = User.get(id); // Must reload object
.. // do stuff
user.save(flush:true)
}
} catch (OptimisticLockingException ex) {
...
}
I hope this is of use to someone else!
I spent some time working on this problem and have written a more complete solution to handle the case of an optimistic locking exception in Grails.
Firstly, though the exception reported in the stack trace is StaleObjectStateException, the actual exception that gets thrown is HibernateOptimisticLockingFailureException (not "OptimisticLockingException"). Secondly, if you want to generalize this to handle arbitrary closures which modify domain objects, you need to rethrow exceptions thrown inside the closure.
The following static function will take an object and a closure that operates on the object, save it, and if it fails, retry again until it succeeds:
public static retryUpdate(Object o, Closure c) throws Exception {
def retVal
int retryCount = 0
while (retryCount < 5) {
try {
Model.withTransaction { status ->
retVal = c(status)
o.save()
}
return retVal
} catch (HibernateOptimisticLockingFailureException e) {
log.warn "Stale exception caught saving " + o
if (++retryCount >= 3) { // if retry has failed three times, pause before reloading
Thread.sleep(1000)
}
o.refresh()
} catch (UndeclaredThrowableException e2) {
// rethrow exceptions thrown inside transaction
throw e2.getCause()
}
}
return null
}
Model in this case is any GORM model class, doesn't matter which one. In particular it doesn't matter if it is the class of the passed-in object.
Example of use:
AnotherModelClass object = AnotherModelClass.get(id)
retryUpdate(object) {
object.setField("new value")
}