What is proper way to get DB connection in Grails 3?
For grails 2 following code has works:
((SessionImpl) sessionFactory.getCurrentSession()).connection() // sessionFactory initialized in bootstrap
But after migration to Grails 3 sometimes I see exceptions in the log:
java.sql.SQLException: Operation not allowed after ResultSet closed at
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957) at
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:896) at
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:885) at
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:860) at
com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:743) at
com.mysql.jdbc.ResultSetImpl.findColumn(ResultSetImpl.java:1037) at
com.mysql.jdbc.ResultSetImpl.getLong(ResultSetImpl.java:2757) at
com.mchange.v2.c3p0.impl.NewProxyResultSet.getLong(NewProxyResultSet.java:424)
at java_sql_ResultSet$getLong$3.call(Unknown Source)
It happens for 0,01% of requests
Grails 3.2.11
Gorm 6.0.12
I guess it depends on where you need it, but you can inject a DataSource into a service.
javax.sql.DataSource dataSource
Then you can just use
dataSource.getConnection()
Also be aware of the changes to flush mode in GORM 6 (http://gorm.grails.org/6.0.x/hibernate/manual/ section 1.2.1). If an upstream save/commit is failing, your result set could be incidentally closed and trigger an error that looks like this while not really have anything to do with this particular line of code at all. I'd (very temporarily) set back to the old flush mode and see if the problem goes away, before tracking much more down!
From grails docs, you can get the actual dataSource bean. From that you can access the connection or use it to query your db
import groovy.sql.Sql
def dataSource
println "connection: ${dataSource.connection}"
Sql sql = new Sql(dataSource)
sql.eachRow("SELECT * FROM note") { row ->
println "row: ${row}"
}
Use 'dataSourceUnproxied' to avoid Hibernate transaction and session issues:
def dataSourceUnproxied
For executing queries inside current hibernate transactions following construction can be used:
sessionFactory.currentSession.doWork {connection ->
new Sql(connection).execute(query, params)
}
Related
Does this mean we can not call some thing like this via Java API?
I get error - "Caused by: org.neo4j.graphdb.QueryExecutionException: Cannot perform schema updates in a transaction that has performed data updates."
This happens when I call schema update from a procedure call via neo4j console.
try (Transaction tx = db.beginTx()) {
String query = "CREATE INDEX ON :" + lbl + "(" + name + ")";
db.execute(query);
tx.success();
}
The Cypher query calling the procedure is already executed in a transaction, and there are no nested transactions in Neo4j: when you call db.beginTx(), you're getting the existing transaction, and it's not actually necessary unless you need the Transaction object (e.g. to create locks).
Anyway, even though it's not explicitly documented, it's apparently not possible to manipulate the schema from Neo4j procedures. You could say that it fails the use case of
To provide access to functionality that is not available in Cypher, such as manual indexes and schema introspection.
I created a test procedure similar to yours:
public class IndexProcedure {
#Context
public GraphDatabaseService db;
#Procedure
#PerformsWrites
public void index(#Name("label") String label, #Name("property") String property) {
db.schema().indexFor(Label.label(label)).on(property).create();
}
}
and ran it from the shell in the simplest Cypher query:
CALL my.package.index('Node', 'name');
Without the #PerformsWrite annotation, I get the following (expected) exception:
WARNING: Failed to invoke procedure my.package.index: Caused by: org.neo4j.graphdb.security.AuthorizationViolationException: Schema operations are not allowed for READ transactions.
With the annotation, I get the same exception as you:
WARNING: Failed to invoke procedure my.package.index: Caused by: org.neo4j.graphdb.QueryExecutionException: Cannot perform schema updates in a transaction that has performed data updates.
I guess the rationale is that setting up the schema is mostly a one-time operation that doesn't really need a procedure: if you're going to execute some Cypher query to call the procedure, you might as well run the script which creates the constraints and indices.
There could also be technical constraints: index creation is asynchronous and probably doesn't participate in the transaction (can you rollback the creation of an index?).
Or maybe it's just a bug? We should get someone from Neo to confirm.
Update: it will supposedly be fixed in Neo4j 3.1 when it's released, per a discussion on SlackHQ.
What is the correlation between Spring org.springframework.transaction.annotation.Transactional annotation and Neo4j OGM org.neo4j.ogm.session.Session.getTransaction() method.
I'm trying to access the current transaction via session.getTransaction() inside of the method annotated with Spring #Transactional but always getting null.
I have added a following code inside of my Spring MVC RestController method:
Transaction tx = session.beginTransaction();
try {
for (int i = 0; i < 10; i++) {
initializeNode(node);
}
}
tx.commit();
} catch (Throwable th) {
logger.error("Error while inserting mock data", th);
th.printStackTrace();
} finally {
tx.close();
}
in case of the following method:
private void initializeNode(TestNode node) {
System.out.println(session.getTransaction());
}
it prints current tx - so far everything is okay.
But in case of the following method:
private void initializeNode(TestNode node) {
System.out.println(session.getTransaction());
User admin = userDao.findByUsername("admin");
}
first time it prints current tx and then null... transaction disappear before commit for a some reason..
this is findByUsername method:
#Service
#Transactional
public class UserDaoImpl implements UserDao {
#Override
#Transactional(readOnly = true)
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
...
}
Right after that on commit I'm getting a following exception:
org.neo4j.ogm.exception.TransactionManagerException: Transaction is not current for this thread
at org.neo4j.ogm.session.transaction.DefaultTransactionManager.commit(DefaultTransactionManager.java:100)
at org.neo4j.ogm.transaction.AbstractTransaction.commit(AbstractTransaction.java:83)
at org.neo4j.ogm.drivers.embedded.transaction.EmbeddedTransaction.commit(EmbeddedTransaction.java:77)
What am I doing wrong ? Why transaction disappears ?
There are several issues and themes going on in this question. I will try and break them down and hopefully at the end it will all make sense.
As of the latest release of Spring Data Neo4j (4.1.x) there is no correlation between Spring's #Transactional and the Neo4j OGM's Session.getTransaction() or Session.beginTransaction() when called directly.
In your first two code blocks you are completely managing your OGM session lifecycle directly. Spring is not involved at all at this point and as you say it executes as expected.
In your updated third code block you are now expecting the session that you have manually opened to work with your Spring managed DAO. What will happen here is depends on the Neo4j Driver you are using with SDN but essentially because your DAO has the #Transactional annotation, Spring will intercept the call and start a brand new transaction all on its own on top of the one you are manually managing. At this point, we can't make any guarantees about the behaviour but the easiest explanation would be to say that it will be unexpected (again, depending on the driver used).
So how can you fix this?
I'm going to assume you want to use Spring Transactions and Spring Data Neo4j. If that's the case you will want to start by:
Changing your DAO to use Spring Data Repositories. This gives you a lot of free persistence functionality like finders, saves, deletes etc.
Putting the #Transactional annotation around the unit of work you want to accomplish. You might have a method that calls userRepository.findByUserName(), modifies that user and calls userRepository.save(user). In a web environment this is typically some sort of service method.
Removing any code that manually starts or ends an OGM session transaction.
You can find a very short code sample here and a longer code sample here.
A more comprehensive guide can also be found here.
In Spring Data Neo4j 4.2.x we hope to introduce some more powerful and friendlier #Transactional behaviour so keep posted for that update.
I am not new to hibernate, concurrrency problems, and stale exceptions.
Im working in grails with GORM in a high concurrent environment. Optimistic locking is on and im using static lock method whithin a transactional service.
for(Entity entity : entityList) {
//refresh and lock for update
entity.refresh();
entity = Entity.lock(entity.id)
entity.setSomeData(new Date());
entity.save()
}
It is somehow possible to have an HibernateOptimisticLockingFailureException in this scenario??
Please note the stack trace:
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxxxx.xxx.xxxxx.Entity#48225]
at org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy.lock(PessimisticWriteSelectLockingStrategy.java:94)
at org.hibernate.persister.entity.AbstractEntityPersister.lock(AbstractEntityPersister.java:1954)
at org.hibernate.event.internal.AbstractLockUpgradeEventListener.upgradeLock(AbstractLockUpgradeEventListener.java:102)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromSessionCache(DefaultLoadEventListener.java:560)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:430)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:212)
at org.hibernate.event.internal.DefaultLoadEventListener.lockAndLoad(DefaultLoadEventListener.java:391)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:153)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1070)
at org.hibernate.internal.SessionImpl.access$2000(SessionImpl.java:176)
at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2544)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:1023)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate$5.doInHibernate(GrailsHibernateTemplate.java:282)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:179)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.lock(GrailsHibernateTemplate.java:279)
at org.codehaus.groovy.grails.orm.hibernate.HibernateGormStaticApi.lock(HibernateGormStaticApi.groovy:221)
at com.xxxxx.xxx.xxxxx.Entity.lock(Entity.groovy)
How is possible for static Entity.lock() to throw a stale object exception??
I'm trying to develop a J2EE application (in WildFly 8.2) that uses Neo4J 2.2.1 embedded .
Since i'm migrating it from Neo4J 1.9, the application uses the indexing legacy system.
I'm experiencing problems during some operations that shouldn't returns exceptions related to a transaction.
For example:
for ( Iterator iterator = node.getPropertyKeys().iterator(); iterator.hasNext(); ) {
key = (String)(iterator.next());
....
}
The stacktrace:
File (71):ThreadToStatementContextBridge.java - org.neo4j.kernel.impl.core.ThreadToStatementContextBridge.assertInUnterminatedTransaction
File (104):ThreadToStatementContextBridge.java - org.neo4j.kernel.impl.core.ThreadToStatementContextBridge.getTopLevelTransactionBoundToThisThread
File (111):ThreadToStatementContextBridge.java - org.neo4j.kernel.impl.core.ThreadToStatementContextBridge.getKernelTransactionBoundToThisThread
File (64):ThreadToStatementContextBridge.java - org.neo4j.kernel.impl.core.ThreadToStatementContextBridge.instance
File (785):InternalAbstractGraphDatabase.java - org.neo4j.kernel.InternalAbstractGraphDatabase$8.statement
File (358):NodeProxy.java - org.neo4j.kernel.impl.core.NodeProxy.getPropertyKeys
Note that i got the same error if i call the following:
Index<Node> indexNode = ...
...
indexNode.get("users", "test#test.com").getSingle()
where indexNode is a previous created index in a transaction.
Any ideas?
Thank you
The most impactful breaking changes from Neo4j 1.9 -> 2.0 was mandatory read transactions. Whenever you do read operations you need to have a transaction around that.
try (Transaction tx=graphDatabaseService.beginTx()) {
// read stuff - either graph or index operations
...
tx.success(); // make sure to have this, otherwise trouble might happen in case of nested transactions
}
I try to execute raw SQL in Grails with this code:
class PlainSqlService {
def dataSource // the Spring-Bean "dataSource" is auto-injected
def newNum = {
def sql = new Sql(dataSource) // Create a new instance of groovy.sql.Sql with the DB of the Grails app
def q = "SELECT a.xaction_id, a.xdin FROM actions a WHERE a.is_approved = 0"
def result = sql.rows(q) // Perform the query
return result
}
}
But I get this exception at runtime.
sql object is not null!
How can I debug it?
2011-02-13 15:55:27,507 [http-8080-1] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [GET] /moderator/login/index
Stacktrace follows:
java.lang.NullPointerException
at moderator.PlainSqlService$_closure1.doCall(PlainSqlService.groovy:17)
at moderator.PlainSqlService$_closure1.doCall(PlainSqlService.groovy)
at moderator.LoginController$_closure1.doCall(LoginController.groovy:29)
at moderator.LoginController$_closure1.doCall(LoginController.groovy)
at java.lang.Thread.run(Thread.java:662)
It's hard to tell what's going on from the limited code you're providing, but there are some things to check. Is the service injected into the controller with a class-scope field "def plainSqlService" like you have here for the dataSource, or are you calling new PlainSqlService()? If you're creating a new instance then the dataSource bean won't be injected and the groovy.sql.Sql constructor won't fail, but queries will.
One thing to try is grails clean - whenever something like this that should work doesn't, a full recompile often helps.
One important but unrelated point - you should never use Closures in services. Controllers and taglibs require that actions and tags be implemented with a Closure, but a Service is just a Spring bean defined in Groovy. Spring knows nothing about Closures and since they're just a field that Groovy executes as if it were a method, any proxying that you're expecting from Spring (in particular transactional behavior, but also security and other features) will not happen since Spring only looks for methods.
So newNum should be declared as:
def newNum() {
...
}