I set up a very basic Grails 3 web-app, using Hibernate plugin 4.3.10.7 (GORM 5.0.1 release), connected to a PostgreSQL db using jdbc. Below you can find code for Cluster domain class and the dedicated service.
It happened to call the createCluster method twice with the same slug argument:
Code:
clusterService.createCluster('Cluster 1', 'cl01')
clusterService.createCluster('Cluster 2', 'cl01')
resulting in the following exception
Code:
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ERROR: duplicate key value violates unique constraint "uk_9ig73x9wropf95ogrffcvyahk"
Detail: Key (slug)=(cl01) already exists.
[...]
ERROR org.hibernate.AssertionFailure - HHH000099: 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 myPackage.Cluster entry (don't flush the Session after an exception occurs)
[...]
null id in myPackage.Cluster entry (don't flush the Session after an exception occurs). Stacktrace follows:
org.hibernate.AssertionFailure: null id in myPackage.Cluster entry (don't flush the Session after an exception occurs)
at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93) ~[grails-core-3.1.1.jar:3.1.1]
at com.usablenet.utest.AdminController.clusters(AdminController.groovy:28) ~[main/:na]
at grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter.doFilter(GrailsAnonymousAuthenticationFilter.groovy:53) ~[spring-security-core-3.0.3.jar:na]
at grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter.doFilter(MutableLogoutFilter.groovy:62) ~[spring-security-core-3.0.3.jar:na]
at grails.plugin.springsecurity.web.SecurityRequestHolderFilter.doFilter(SecurityRequestHolderFilter.groovy:58) ~[spring-security-core-3.0.3.jar:na]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[na:1.8.0_60-ea]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_60-ea]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_60-ea]
I assumed that the violation of the unique constraint would had been intercepted by the .validate() method, but apparently I was wrong. Ok, so I added a try/catch but it basically has no effect, the exception is not wrapped and I basically get a 500 Internal Error.
I'm not totally sure this is a Hibernate bug, but as my setup is extremely basic I'm not sure where is the issue.
My questions are 2:
- why isn't this exception catched as i would expect?
- what does null id / don't flush the Session after an exception occurs error really mean? I'm not explicitly flushing the session, and of course the new record's id is null...
After checking a few threads pointing out the same issue, 2 of them hinted to set JTA to TRUE for the data source that Hibernate is using. Said that I have no clue how to do that, as far as I understood JTA is more oriented to manage multiple transactions among multiple databases, so I'd definitely say it's not my case... Am I right?
Thanks in advance!
Cluster.groovy (domain class)
Code:
class Cluster {
String name
String slug
static constraints = {
name blank: false, unique: true
slug blank: false, unique: true
}
}
ClusterService.groovyCode:
@Transactional
class ClusterService {
public Cluster createCluster(String name, String slug, boolean flush = false) {
Cluster cluster = new Cluster(name: name, slug: slug)
try {
if(cluster.validate())
cluster.save(flush: flush)
} catch(Exception e) {
e.printStackTrace()
}
return cluster
}
}
application.yml (db configuration)
Code:
dataSource:
pooled: true
driverClassName: 'org.postgresql.Driver'
dialect: 'org.hibernate.dialect.PostgreSQLDialect'
username: 'postgres'
password: 'postgres'
environments:
development:
dataSource:
dbCreate: create
url: jdbc:postgresql://localhost:5432/myapp