Exception on delete hits the console in Grails - grails

I'm having a little bit of trouble with console outputs for a delete statement.
I'm using Grails 2.3.11 for this project.
The idea ist quite simple and works, but there is an unwanted output to the console.
The project enables the user to delete a person, which has no ties to other domain objects. To achieve this, I use the following code:
def person = Person.get(params.id)
try {
person.delete(flush: true)
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
def returnData = [:]
returnData.put("message", "Person is undeletable")
returnData.put("success", false)
render returnData as JSON
}
If it doesn't have any ties in the db, the object gets deleted. If it does, it doesn't get deleted, which is what I want!
If an object couldn't be deleted the server response is a status 200 with the JSON, so that works fine!
But it still sends a stacktrace to the console/log, which is in my opinion unnecessary.
Is there a way to get around that, that I havn't seen?
Thanks in advance!
Here's the relevant excerpt of the stacktrace:
| Error 2015-01-29 16:23:38,291 [http-bio-8080-exec-12] ERROR util.JDBCExceptionReporter - Cannot delete or update a parent row: a foreign key constraint fails (`db`.`table`, CONSTRAINT `FKFC41E6E036872635` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`))
...
| Error 2015-01-29 16:23:38,294 [http-bio-8080-exec-12] ERROR events.PatchedDefaultFlushEventListener - Could not synchronize database state with session
Message: could not delete: [Person#13454]
Caused by MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`db`.`table`, CONSTRAINT `FKFC41E6E036872635` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`))

Related

Unable to catch database exceptions in Grails 3

I'm facing an odd issue after migrating an app from Grails 2.5 to Grails 3.1 - database exceptions never bubble up to the controller.
The typical scenario looks like this: controller's delete action attempts to remove an entry, but a database-level constraint prevents the delete. This leads to an exception (see below) - in Grails 2.5, if uncaught, this would lead to "Error 500 internal server error". Now, we have a custom mapping for these, so even in case an uncaught error occurs, it is handled in a user-friendly way and the user knows something happened.
In Grails 3, this stopped working. The exception is logged, but would never bubble up to the controller. So in this specific case, our controller just goes on and returns "204" HTTP status code as if nothing happened, and the end user doesn't even know something went wrong.
I'm puzzled and I think this must be something stupid I'm missing, but can't figure it out. I have tested this Grails 2.5.5, 3.1.16, 3.2.12 and 3.3.2, but all tested 3.x releases behaved pretty much the same way. Also tested with both Hibernate 4 and 5, and tried H2 and PostgreSQL but I'm getting the same behavior regardless.
The easiest way to reproduce is to manually add a FK relationship (with delete no action) to the hibernate-generated database schema and attempt to delete via the controller (example #1 below) but the issue may also occur with some many-to-many relationships or other scenarios such as persisting a new entry (and failing at the database level).
UPDATE #1, April 25, 2018: To create a minimal reproducible test case using PostgreSQL as the database and Grails 3.3.x, just create a blank grails application via grails create-app testapp. Then adjust the application.yml to configure data source for your PostgreSQL instance, and use dbCreate: validate mode as the schema is provided below.
Add one minimal domain object:
class Book {
String name
}
And a controller to test object deletion, i.e. BookController:
class BookController {
#Transactional
def delete(Long id) {
Book.get(id).delete()
render "Book $id deleted"
}
}
The schema below creates 2 tables with a FK and a few entries. Book with id 1 will fail to be removed, books with id 2 and 3 will be removed successfully. Controller will return 200 OK in all 3 cases. You can test using the default UrlMappings via curl calls, i.e. curl http://localhost:8080/book/delete/1.
The database schema follows:
CREATE TABLE book (
id bigint NOT NULL,
version bigint NOT NULL,
name character varying(255) NOT NULL,
CONSTRAINT book_pkey PRIMARY KEY (id)
);
CREATE TABLE chapter (
id bigint NOT NULL,
book_id bigint NOT NULL,
name character varying(255),
CONSTRAINT chapter_pkey PRIMARY KEY (id),
CONSTRAINT fk_book FOREIGN KEY (book_id) REFERENCES book(id)
);
INSERT INTO book VALUES (1, 0, 'Book 1');
INSERT INTO book VALUES (2, 0, 'Book 2');
INSERT INTO book VALUES (3, 0, 'Book 3');
INSERT INTO chapter VALUES (1, 1, 'Book 3 Chapter');
CREATE SEQUENCE hibernate_sequence START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
SELECT pg_catalog.setval('hibernate_sequence', 4, true);
For the sake of completeness, here's the stack trace:
2018-04-25 16:23:22.462 ERROR --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: update or delete on table "book" violates foreign key constraint "fk_book" on table "chapter"
Detail: Key (id)=(1) is still referenced from table "chapter".
2018-04-25 16:23:22.473 ERROR --- [nio-8080-exec-1] org.hibernate.internal.SessionImpl : HHH000346: Error during managed flush [could not execute statement]
2018-04-25 16:23:22.720 ERROR --- [nio-8080-exec-1] o.g.web.errors.GrailsExceptionResolver : PSQLException occurred when processing request: [GET] /test/delete/1
ERROR: update or delete on table "book" violates foreign key constraint "fk_book" on table "chapter"
Detail: Key (id)=(1) is still referenced from table "chapter".. Stacktrace follows:
java.lang.reflect.InvocationTargetException: null
at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:211)
at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:188)
at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [fk_book]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:241)
at org.springframework.orm.hibernate5.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:755)
at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:590)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:765)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:734)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:150)
at grails.gorm.transactions.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:91)
... 14 common frames omitted
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:112)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3311)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3548)
at org.hibernate.action.internal.EntityDeleteAction.execute(EntityDeleteAction.java:98)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:586)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:460)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1295)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:468)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3135)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2352)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:491)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:582)
... 18 common frames omitted
Caused by: org.postgresql.util.PSQLException: ERROR: update or delete on table "book" violates foreign key constraint "fk_book" on table "chapter"
Detail: Key (id)=(1) is still referenced from table "chapter".
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2433)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2178)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:306)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365)
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:155)
at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:132)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
... 36 common frames omitted
UPDATE #2, April 25, 2018: the fact that there is a database table that Hibernate isn't aware of was the point of the above example. It was an easy way to force a database-level exception that would only be logged, but wouldn't make it all the way up to the controller so on HTTP level it would appear as if no error occurred.
UPDATE #3, April 25, 2018: Another test case for Grails 3.3 that doesn't rely on any manual database schema "hacks" is below, with a many-to-many relationship between the same domain (User). It consists of 2 domain classes:
class User {
String name
static hasMany = [relationships: Relationship]
static mappedBy = [relationships: 'user']
static mapping = { table 'table_user' }
}
class Relationship implements Serializable {
User user
User related
static belongsTo = [ user: User, related: User ]
static mapping = {
table 'table_relationship'
id composite: ['user', 'related']
version false
}
}
And a minimal controller:
class TestController {
#Transactional
def delete(Long id) {
User.get(id).delete()
render "User $id deleted"
}
}
This can be reproduced with PostgreSQL or H2, just use the create-drop mode and the following BootStrap code to create 2 User entries:
def init = { servletContext ->
def user1 = new User(name: 'User 1').save()
new User(name: 'User 2', relationships: [new Relationship(related: user1)]).save()
}
Attempting to delete user 2 and then user 1 will work OK. Attempting to first remove user 1 will fail with an exception, yet the controller would still return "200 OK". Just try curl http://localhost/test/delete/1.
For convenience you can download the zipped archive with a runnable Grails 3.3.5 app here: https://www.dropbox.com/s/pycwuxm7r0wyxem/grails3_exception_issue_testapp.zip?dl=0
If you are going to use #Transactional you should use grails.gorm.transactions.Transactional instead of grails.transaction.Transactional.
Separate from that, the problem doesn't appear to be that the exception doesn't make its way up to the controller as much as what is really happening is the transaction isn't being committed until after you call render and the exception doesn't get thrown until the transaction is committed. Calling .delete(flush: true) will show different behavior but putting the transaction boundary in the controller is a bad idea anyway. A better plan is to put your database interactions in a transactional service.
I put your code in the project at https://github.com/jeffbrown/many33. The commit at https://github.com/jeffbrown/many33/commit/f8804c8793b399994c34043c1d340e4bf0d462cd shows a better way to organize this code and yields less surprising behavior.
I hope that helps.

Schema Export Errors while launching grails application

I am trying to follow the Grails application from the book Grails 2 - A Quick start guide. The grails version that i am using is 2.4.4 on Ubuntu 14.04 with the open jdk 7
I am getting the following error
Error 2015-03-09 20:05:02,117 [localhost-startStop-1] ERROR hbm2ddl.SchemaExport - HHH000389: Unsuccessful: alter table tek_event drop constraint FK_1xbf5b7edlnmgmrc90jhbyvg7 if exists
| Error 2015-03-09 20:05:02,118 [localhost-startStop-1] ERROR hbm2ddl.SchemaExport - Table "TEK_EVENT" not found; SQL statement:
There is nothing in my application except two domain classes and scaffolded controllers ...
Here are the domain classes TekUser and TekEvent
class TekEvent {
String city
String name
TekUser organizingUser
String venue
Date startDate
Date endDate
String description
String toString(){
"$name, $city"
}
static constraints = {
name()
city()
description maxSize:5000
organizingUser()
venue()
startDate ()
endDate()
}
}
And the TekUser Domain class
class TekUser {
String fullName
String userName
String password
String email
String website
String bio
String toString(){
fullName
}
static constraints = {
fullName()
userName()
email()
website()
bio maxSize:5000
}
}
The controllers for them are pretty barebones
class TekEventController {
def scaffold = TekEvent;
}
class TekUserController {
def scaffold = TekUser;
}
I am not able to follow what is going wrong here ...or is it a benign error that I could just ignore.
Also this started happening when I changed the datatype of organizingUser in the TekEvent class from String to TekUser
These are ignorable errors - there's nothing bad happening. Unfortunately the Hibernate developers changed the logging strategy in Hibernate 4, and that's what you're seeing.
In Hibernate 3, using "create-drop" means to drop all current mapped tables and associated objects (sequences, joins, etc.), then run the create statements, and if the jvm shuts down cleanly, drop all of the tables again at the end. "create" is slightly different in that it does all of the drops and all of the creates, but doesn't do anything at shutdown. This is one I use often since it lets me view data in disk-based databases after the app shuts down.
So the core issue is that the initial drop statements are missing the "if exists" clause, and sometimes not everything that Hibernate is trying to drop exists. So you get these scary looking errors, but they're harmless since they're just telling you that some of the things that were supposed to be deleted were never created.
In Hibernate 3, these errors were stored in a list of strings that you could access after running the script. In Hibernate 4 they changed it to log the errors, I assume since not all errors in that phase are ignorable, so it's better to have false positives in with the real issues than everything hidden in a list that is probably rarely checked.
You can choose to ignore these (at the risk of also ignoring unrelated real error messages), or use a custom Dialect that generates the proper SQL. One example is here

Why does EF 6.01 not give me a DbEntityValidationException Exception?

When I update my SQL 2012 database via EF 6.01 SaveChanges, with a string field that is too long, I get an Exception as expected. What I would like to do is drill into the Exception to find the offending table and column as the innermost SqlException merely tells me -
String or binary data would be truncated.
but not which column or table. I have code like below ready to tell me about any validation errors, but do not get such an Exception.
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
}
The Exception I get is nested as DbUpdateException containing an UpdateException containing a SqlException and none of these have any column information. Why do I not get a DbEntityValidationException? Is there any other way to find the offending column?
This exception you're getting is coming directly from SQL, being passed through Entity Framework. From MSDN about DbEntityValidationException:
Represents an exception thrown from SaveChanges() when the validation of entities fails.
(emphasis my own). The validation from Entity Framework is passing, but the actual SQL statement is failing since the data you're passing it is too long for a column. SQL does not return the column that would be truncated either. It's just a plain text message of 'String or binary data would be truncated.'
Your best bet, go through the columns and ensure the lengths on strings in your code match the lengths you have set in the SQL columns.
Apparently a DbEntityValidationException is not thrown because EF validation is not done for some reason, so I have added this code before doing the SaveChanges (via TxRepository.Commit) and throw my own custom EfValidationException containing the ValidationErrors if there are any. I have tested, and this works allowing me to log the problematic column.
// validate the changes
var lTxValidationErrors = TxRepository.mDbContext.GetValidationErrors();
if (lTxValidationErrors.Count() > 0)
{
// these changes will not commit so throw an error
throw new EfValidationException(lTxValidationErrors);
}
else
{
// commit the new data to the database
TxRepository.Commit();
}

Grails 2.1 dateCreated null intermittently on domain save

tl:dr; This is a bit involved of a problem, any advice is welcome, appreciate reading in advance :)
My coworkers and I have been struggling a bit with an odd behavior in our batch processing application. We recently upgraded it from Grails 1.3.7 to 2.1
The stacktrace is showing the following error:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException:
Cannot insert the value NULL into column 'date_created',
table 'dev.dbo.notification_log'; column does not allow nulls. INSERT fails.
...
[quartzScheduler_Worker-1] [||] ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)
[quartzScheduler_Worker-1] [||] ERROR listeners.SessionBinderJobListener - Cannot flush Hibernate Sesssion, error will be ignored
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)
Here is the code of that particular Domain Object (NotificationLog)
class NotificationLog implements Serializable{
Date dateCreated
Notification notification
NotificationDeliveryState deliveryState
String message
static mapping = {
message type: 'text'
}
}
What's strange, however, is this error doesn't occur EVERY time that domain object is persisted, and we only have one place in the code that object is ever persisted, shown below:
class NotificationLogService {
boolean transactional = true
def logNotification(Notification notification, message, deliveryState) {
def notificationLog = new NotificationLog(
notification: notification,
deliveryState: deliveryState,
message:message
)
try{
notificationLog.save(failOnError:true)
} catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
}
}
}
We've found a 'hack' of a workaround in the below SO question where we are no longer periodically seeing the error in the logs, by adding this to the Domain Object
static mapping = {
autoTimestamp true
}
But this isn't the only domain we're seeing with the SAME periodic failure to save (thus, I need to add the mapping to other domains), and if this truly is necessary for dateCreated to function properly in Grails 2.1, I need to add it to a LOT more domains!
Worse, I can't reproduce it in a Unit or Integration test, its only happening on our running Dev and QA instances.
So, 2 Questions:
Does anyone know why this error might be periodically occurring?
If not, is there a way I can globally add this autoTimestamp true mapping to ALL of my project's domain objects (I've been unable to find documentation for how to add it at all, other than to set it to false)
Relevant SO Question:
dateCreated, lastUpdated fields in Grails 2.0
Relevant Grails Maillist discussion
http://grails.1312388.n4.nabble.com/dateCreated-lastUpdated-in-Grails-2-0-td4337894.html
Relevant Grails docs on autoTimestamp GORM properties
http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping
To answer both of the questions:
EDIT Try flush: true while save otherwise autoTimestamp true is the last resort. I have not researched to find out the cause of this issue.
You can set this property in Config.groovy to make it applicable for all domain classes.
grails.gorm.default.mapping = {
autoTimestamp true //or false based on your need
}
Have you tried to manually insert the date when creating a new NotificationLog?
Like this:
class NotificationLogService {
boolean transactional = true
def logNotification(Notification notification, message, deliveryState) {
def notificationLog = new NotificationLog(
dateCreated: new Date(),
notification: notification,
deliveryState: deliveryState,
message:message
)
try{
notificationLog.save(failOnError:true)
} catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
}
}
}

Grails: Accessing GORM from a background thread

I have been struggling with this error for a week now, and I am seriously losing my mind over this! I have tried multible implementations and work-arounds and hacks and what not, but I just keep stubling into just another exception.
I am using the Executor plugin to run a method asynchroniously:
runAsync{
run(...)
}
The method initially deletes some objects:
page.delete(flush:true)
And then later possibly recreating those objects:
def page = new Page(type : Page.TYPE_TABLE, domain : domainVersion.domain, identifier : tableName)
page.save(flush: true, failOnError: true)
But that fails with the following exception:
Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.ramboll.egs.ohs.domain.Domain#1]
The relationship between the Page and Domain is simply implemented by Page having a Domain attribute. No hasMany og belongsTo - as I was discouraged from this in an earlier post due to performance issues.
I think I have tried all thinkable combinations of save, merge, withTransachtion and PersistenceContextInterceptor...
How is this supposed to work? Examples please.
Thanks in advance!
It doesn't appear that working in a new thread is the issue, it looks like a standard validation problem. It's saying that the Page is null, which indicates a validation error since save() returns the instance if it was successful, or null if there's a one or more validation errors. There are a few options:
def page = new Page(type : Page.TYPE_TABLE,
domain: dbUpdate.domainVersion.domain, identifier: tableName)
page.save(flush:true)
if (page.hasErrors()) {
// handle errors
}
else {
def pageVersion = createPageVersion(page, dbUpdate.domainVersion,
con, tableName, dbUpdate.author).save(flush:true)
}
or use failOnError to throw an exception:
def page = new Page(type : Page.TYPE_TABLE, identifier: tableName,
domain: dbUpdate.domainVersion.domain).save(flush:true, failOnError: true)
def pageVersion = createPageVersion(page, dbUpdate.domainVersion,
con, tableName, dbUpdate.author).save(flush:true)

Resources