Neo4j - How to separately manage schema and data transactions? - neo4j

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.

Related

How to call mysql stored procedure with dynamic name in grails

I have requirement like to call the stored procedures to get the data, here i will get the stored procedure name through post data.
I tried using GString but no use, Can any one help me to solve this issue.
Thanks in advance.
This is a weird one, but possible. Grails/GORM doesn't provide a way to execute stored procedures. It's just not part of ORM functionality. And neither does Hibernate. But, Hibernate does provide access to a JDBC connection. So you can get a connection from Hibernate, then create a Groovy Sql instance with the connection, and finally execute your stored procedure. Here's an example:
import groovy.sql.Sql
SomeDomainClass.withNewSession { session ->
session.doWork { java.sql.Connection connection ->
def sql = new Sql(connection)
sql.call("YOUR STORED PROCEDURE SQL HERE")
}
}
It works like this:
Using any of your domain classes, call withSession(Closure) to get a Hibernate session.
With the session, call doWork(Work) to get access to the session's JDBC connection. Now, doWork(Work) expects an implementation of org.hibernate.jdbc.Work. But, if I recall correctly, Groovy can take a closure with the same parameters and coerce it to implement the interface.
Using the connection, create an instance of Sql. You could skip this and query with Java, but Groovy's Sql class is so nice.
Use one of the available Sql.call() methods to execute your stored procedure.
In postgress SQL, it is posible to call stored procedure. The catch is returning a data table so that you can map it to a model. This is as shown bellow.
create function activate(confirmationcode text)
returns TABLE(resultid integer, resultmessage character varying)
language plpgsql
as $$
BEGIN
....
END
This can be invoked as follows in GORM db connections
type FuncResult struct {
Resultid int
Resultmessage string
}
...
fResult := FuncResult{}
err := s.db.Raw("SELECT * from activate(?)", token).Scan(&fResult).Error

Dropwizard Stored Procedure Example

I am new to dropwizard. I am going through different tutorial but I couldnt find good example/tutorial on how to call stored procedure in dropwizard using JDBI.
With JDBI it is possible to specify an object SQL query as a method annotation. This string is treated by the DB as normal SQL. An example DAO to execute a stored procedure might look like:
public interface SomeQueries
{
#SqlQuery("call find_name_procedure(:id)")
String findName(#Bind("id") int id);
}
Where find_name_procedure has been previously defined.
For more information see http://jdbi.org/sql_object_api_queries/
Jooq is an elegant way to call stored procedures from java (See https://dzone.com/articles/using-stored-procedures-with-jpa-jdbc-meh-just-use). You also have a thirdparty module integrating Jooq into Dropwizard
(See http://modules.dropwizard.io/thirdparty/)
So if you're familiar with Maven, you can set up the combination quickly and start validating if it works in your case.
DropWizard JDBI provides two options to make a stored procedure call. Depending on what you want from Stored proc.
If you want resultset from Stored Proc, Andy has answered that already:
public interface DAO
{
#SqlQuery("call sp_name(:id)")
String query(#Bind("id") int id);
}
The other scenario is that your stored proc doesn't return resultset but gives out some output parameters. Here is what you can do:
#SqlCall("{ CALL sp_name(:id, :id2) }")
#OutParameter(name = "out", sqlType = Types.INTEGER)
OutParameters spCal(#Bind("id") long id, #Bind("id2") long id2);
OutParameter annotation may not work in DropWizard as the annotation was introduced in v3, you can use the following for accessing OutParameters:
try (Handle handle = dbi.open()) {
parameters = handle.createCall("CALL sp_name(:IN_Field1,:OUT_field2)")
.bind("IN_Field1", filed1)
.registerOutParameter("OUT_field2", Types.INTEGER)
.invoke();
}
And for some real freaky situations where you will need both Outparameters and resultset - DropWizard currently doesn't provide any direct way (Because the JDBI version with dropwizard is old). So, you can get connection by doing below:
handle = jdbi.open()
handle.getConnection()
And then do what you want to do with the connection.
Also, one more thing - I will not try to use Hibernate for Stored Proc call. Its just more pain.
I used Zalando Sprocwrapper. I like it and was widely used in the Zalando company to call PostgreSQL functions.
https://github.com/zalando-incubator/java-sproc-wrapper

grails nested transaction bean reset

I'm using grails 1.3.7 together with Oracle 11g and trying to manage inner transactions.
I have a bean Person that is passed to a transactional (Propagation.REQUIRED) service method who makes some modification. Then it is passed to another transactional (propagation = Propagation.REQUIRES_NEW) method that makes some other modification and then throws an Exception.
What I expected to see is the rollback of all the modification of the second service but still valid those of the first one. This is the situation:
//outer transaction
class MyService {
def nestedService
#Transactional(propagation = Propagation.REQUIRED)
public void testRequiredWithError(Person person) {
person.name = 'Mark'
try {
nestedService.testRequiresNewWithError(person)
} catch (RuntimeException e) {
println person.age //this prints 15
println e
}
}
}//end MyService
//inner transaction
class NestedService{
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void testRequiresNewWithError(Person person) {
person.age = 15 //expected after Exception will be discarded
throw new RuntimeException("Rollback this transaction!")
}
}
then I run grails console and check on the DB after it ends.
...
def p = Person.get(671)
def myService = ctx.myService
println p.name //'John'...from DB
println p.age //25...from DB
myService .testRequiredWithError(p)
println p.name // 'Mark'....correct
println p.age // 15....UNEXPECTED..
//same result checking on the DB after console ends and transaction flushes
I tried to use Propagation.NESTED after activating it in the bootstrap by transactionManager.setNestedTransactionAllowed(true)and use savepoints like in this post
grails transaction set savepoint
but still got same result.
What am I missing????
Thank you in advance.
I'm a little late to the party but in case you haven't found your answer I think I know why you're having issues with transactions.
I looked at your link to the discussion about your issues with savepoints. According to that discussion you are using MySQL as your datasource. MySQL does not support transactions by default and in order to make it do so, you need to create your tables in a special way. I have provided a link below that explains how you should create your tables in MySQL when you want to use transactions:
http://www.tutorialspoint.com/mysql/mysql-transactions.htm
EDIT: This article suggests setting the type of your table to InnoDB in order to support transactions. Here is an example of what that looks like:
mysql> create table tcount_tbl
-> (
-> tutorial_author varchar(40) NOT NULL,
-> tutorial_count INT
-> ) TYPE=InnoDB;
It might be worth noting that this is not the only type that supports transaction but is the most common. MySQL 5.5 and greater should create tables as type InnoDB automatically.
Hope this helps!
I'd test for p.isAttached() as transaction rollback detaches the domain object from the Hibernate session. Also in the test I'd reload the domain object from database - effectively p = Person.get(671) again to reload data from database.
I think the reason the age property is set to 15 after the test is that after the exception the domain object and the database are out of sync (and the domain object is detached).
For more see: https://weblogs.java.net/blog/blog/davidvc/archive2007/04/jpa_and_rollbac.html

Not able to access iterator() from RestTraverser, it gives exception java.lang.IllegalAccessError

I am implementing traversal framework using neo4j java-rest-binding project.
Code is as follows:
RestAPI db = new RestAPIFacade("http://localhost:7474/db/data");
RestNode n21 = db.getNodeById(21);
Map<String,Object> traversalDesc = new HashMap<String, Object>();
traversalDesc.put("order", "breadth_first");
traversalDesc.put("uniqueness", "node_global");
traversalDesc.put("uniqueness", "relationship_global");
traversalDesc.put("returnType", "fullpath");
traversalDesc.put("max_depth", 2);
RestTraverser traverser = db.traverse(n21, traversalDesc);
Iterable<Node> nodes = traverser.nodes();
System.out.println("All Nodes:"); // First Task
for(Node n:nodes){
System.out.println(n.getId());
}
Iterable<Relationship> rels = traverser.relationships();
System.out.println("All Relations:"); // Second Task
for(Relationship r:rels){
System.out.println(r.getId());
}
Iterator<Path> paths = traverser.iterator(); // Third Task
while(paths.hasNext()){
System.out.println(paths.next());
}
I need to do 3 tasks as commented in the code:
Print all the node IDs related to node no. 21
Print all the relation IDs related to node no. 21
Traverse all the paths related to node no. 21
Tasks 1 & 2 are working fine.
But when I try to do traverser.iterator() in 3rd task it throws an Exception saying:
java.lang.IllegalAccessError: tried to access class org.neo4j.helpers.collection.WrappingResourceIterator from class org.neo4j.rest.graphdb.traversal.RestTraverser
Can anyone please check why this is happening or if I am doing wrong then what is the right method to do it.
Thanks in Advance.
I don't believe using the Neo4j Traversal Framework via the REST DB binding is properly supported, nor is it advisable. If you traverse via REST, each node and each relationship will be retrieved over the network as the traversal proceeds, incurring a tremendous overhead for the traversal.
Edit: The above is not true, the REST traverser is smarter than I thought.
In general, it will be faster to use Cypher, and access the Neo4j Server using JDBC. Read more about JDBC here: https://github.com/neo4j-contrib/neo4j-jdbc
If you really want to use the Traversal Framework, you should use Server Extensions, which allow you to design a traversal to run on the server itself, and then only move the result of the traversal over the network. Read more about server extensions here: http://docs.neo4j.org/chunked/stable/server-unmanaged-extensions.html

entity framework + advantage database: UDFs

I am using VS2010, Entity Framework 4.0, and Advantage v. 10 in my application. I am trying to make a UDF I have defined in my Advantage DB available to my application code. The designer does not show the UDF under stored procs in the "Update Model from Database" wizard as I would expect it to. So I manually added the UDF to the SSDL as follows:
<Function Name="Test" ReturnType="numeric" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion">
<Parameter Name="PartID" Type="integer" Mode="In"/>
</Function>
I also added a CLR method stub:
[EdmFunction("namespace.Store", "Test")]
public static decimal Test(int partID)
{
throw new InvalidOperationException("Call from within an L2E query");
}
I can see the function in my Linq-to-Entities statement; however, the generated SQL is not valid. Using ToTraceString, the UDF call looks something like this:
"namespace.Store"."Test"("Project3"."PartID") AS "C4"
This gives me the following error:
System.Data.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> Advantage.Data.Provider.AdsException: Error 7200: AQE Error: State = 42000; NativeError = 2117; [iAnywhere Solutions][Advantage SQL Engine]Unexpected token: Scalar function name should not be delimited.
It works fine if I run the generated SQL in Advantage Data Architect and correct the function name like so:
Test("Project3"."PartID") AS "C4"
Is there anyway to tell Entity Framework to generate the correct SQL? Am I doing something wrong in the definition of the function in the SSDL?
Thanks in advance.
You need to change your function element to have BuiltIn="true". User Defined Functions are not quoted in the Advantage SQL grammar.
It looks good. Only thing i would recommend is to change the decimal to be nullable but i doubt that would fix the problem because then you would see a different exception. its worth a try. I do covered the function a while back.
http://weblogs.asp.net/zeeshanhirani/archive/2010/04/08/calling-user-defined-database-function-from-linq.aspx

Resources