NullPointerException while loading data from Neo4j database with OGM driver - neo4j

I am using Neo4j OGM 2.0.4 driver with Java (embedded driver). When I do save(item) on session everything looks all right. Hovewer when I want to load items from database I get this exception:
16:01:23.182 [main] INFO org.neo4j.ogm.service.DriverService - Using driver: org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver
16:01:24.768 [main] DEBUG org.neo4j.ogm.service.Components - Setting driver to: org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver
16:01:24.782 [main] WARN org.neo4j.ogm.session.Neo4jSession - Thread 1: neo4jCMS.entity.Author is not an instance of a persistable class
16:01:24.782 [main] WARN org.neo4j.ogm.session.Neo4jSession - Thread 1: neo4jCMS.entity.Author is not an instance of a persistable class
Saved
Exception in thread "main" java.lang.NullPointerException
at org.neo4j.ogm.MetaData.entityType(MetaData.java:280)
at org.neo4j.ogm.session.Neo4jSession.entityType(Neo4jSession.java:486)
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:60)
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:108)
at org.neo4j.ogm.session.Neo4jSession.loadAll(Neo4jSession.java:152)
at neo4jCMS.TestSite.run(TestSite.java:35)
at neo4jCMS.Application.main(Application.java:20)
While executing:
Author author1 = new Author("Author no 1");
Author author2 = new Author("Author no 2");
session.save(author1);
session.save(author2);
System.out.println("Saved");
Iterable<Author> authors = session.loadAll(Author.class);
for (Author author : authors)
{
System.out.println("Author: " + author.getName());
}
My node class is:
#NodeEntity
public class Author
{
#GraphId
private Long id;
private String _name;
public Author() { _name = "";}
public Author(String name)
{
_name = name;
}
public String getName()
{
return _name;
}
}

First of all you will need to add a no-arg constructor to your Author class in order for the OGM to create objects.
If that does not solve it try rerunning the loadAll code segment again. I have seen the odd Exception on first execution. I thought this was fixed but maybe it's only in 2.0.5-SNAPSHOT.

The issue was in configuration. new SessionFactory() was without argument "myProject.subDirectory". After adding that it works.

Related

Set deserializing in Spring Native image problem

I'm stuck with a problem on a built Spring Native image:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [collection type; class java.util.HashSet, contains [simple type, class java.lang.Object]]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of java.util.HashSet (no Creators, like default constructor, exist): no default no-arguments constructor found
at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 5, column: 14] (through reference chain: com.example.app.payload.request.SignupRequest["roles"])] with root cause
From this description it seems that I need use custom deserializer, but the problem appears only if I run code from native image - same code works perfectly well if run from JAR.
POJOs are very simple:
public class SignupRequest {
#NotBlank
#Size(min = 3, max = 20)
private String username;
#NotBlank
#Size(max = 50)
#Email
private String email;
private Set<String> roles;
#NotBlank
#Size(min = 6, max = 40)
private String password;
// getters & setters no Lombok (but Lombok is in project)
}
Controller uses standard (nothing fancy) annotations:
public ResponseEntity<MessageResponse> registerUser(#Valid #RequestBody SignupRequest signUpRequest)
Has anyone encountered a similar problem?
Finally I found missing part - I have to add HashSet to SerializationHint:
#SpringBootApplication
#SerializationHint(types = {
java.util.HashSet.class
})
public class SpringNativeApplication {
public static void main(String[] args) {
// ...
}
}

Deploying a transaction event listener in a Neo4jDesktop installation

I have created a project that contains an ExtensionFactory subclass annotated as #ServiceProvider that returns a LifecycleAdapter subclass which registers a transaction event listener in its start() method, as shown in this example. The code is below:
#ServiceProvider
public class EventListenerExtensionFactory extends ExtensionFactory<EventListenerExtensionFactory.Dependencies> {
private final List<TransactionEventListener<?>> listeners;
public EventListenerExtensionFactory() {
this(List.of(new MyListener()));
}
public EventListenerExtensionFactory(List<TransactionEventListener<?>> listeners) {
super(ExtensionType.DATABASE, "EVENT_LISTENER_EXT_FACTORY");
this.listeners = listeners;
}
#Override
public Lifecycle newInstance(ExtensionContext context, Dependencies dependencies) {
return new EventListenerLifecycleAdapter(dependencies, listeners);
}
#RequiredArgsConstructor
private static class EventListenerLifecycleAdapter extends LifecycleAdapter {
private final Dependencies dependencies;
private final List<TransactionEventListener<?>> listeners;
#Override
public void start() {
DatabaseManagementService managementService = dependencies.databaseManagementService();
listeners.forEach(listener -> managementService.registerTransactionEventListener(
DEFAULT_DATABASE_NAME, listener));
dependencies.log()
.getUserLog(EventListenerExtensionFactory.class)
.info("Registering transaction event listener for database " + DEFAULT_DATABASE_NAME);
}
}
interface Dependencies {
DatabaseManagementService databaseManagementService();
LogService log();
}
}
It works fine in an integration test:
public AbstractDatabaseTest(TransactionEventListener<?>... listeners) {
URI uri = Neo4jBuilders.newInProcessBuilder()
.withExtensionFactories(List.of(new EventListenerExtensionFactory(List.of(listeners))))
.withDisabledServer()
.build()
.boltURI();
driver = GraphDatabase.driver(uri);
session = driver.session();
}
Then I copy the jar file in the plugins directory of my desktop database:
$ cp build/libs/<myproject>.jar /mnt/c/Users/albert.gevorgyan/.Neo4jDesktop/relate-data/dbmss/dbms-7fe3cbdb-11b2-4ca2-81eb-474edbbb3dda/plugins/
I restart the database and even the whole desktop Neo4j program but it doesn't seem to identify the plugin or to initialize the factory: no log messages are found in neo4j.log after the start event, and the transaction events that should be captured by my listener are ignored. Interestingly, a custom function that I have defined in the same jar file actually works - I can call it in the browser. So something must be missing in the extension factory as it doesn't get instantiated.
Is it possible at all to deploy an ExtensionFactory in a Desktop installation and if yes, what am I doing wrong?
It works after I added a provider configuration file to META-INF/services, as explained in https://www.baeldung.com/java-spi. Neo4j finds it then.

Neo4j OGM returning No Host exception

I am running neo4j-community-2.2.5 locally on my macbook.
I am trying to connect to the code using the neo4j-ogm version : 1.1.2
Here is the session factory:
public class Neo4jSessionFactory {
private final static SessionFactory sessionFactory = new SessionFactory("com.readypulse.rpinfluencernetwork.ogm.model");
private static Neo4jSessionFactory factory = new Neo4jSessionFactory();
public static Neo4jSessionFactory getInstance() {
return factory;
}
private Neo4jSessionFactory() {
}
public Session getNeo4jSession() {
return sessionFactory.openSession("http://localhost:7474", "neo4j", "mypassword");
}
}
I have a entity class :
#NodeEntity(label="Hashtag")
public class Hashtag extends Entity {
#Property(name = "name")
String name;
...
Service :
public interface HashtagService extends Service<Hashtag>{
}
Generic Service:
public abstract class GenericService<T> implements Service<T> {
private static final int DEPTH_LIST = 0;
private static final int DEPTH_ENTITY = 1;
private Session session = Neo4jSessionFactory.getInstance().getNeo4jSession();
public Iterable<T> findAll() {
return session.loadAll(getEntityType(), DEPTH_LIST);
}
public T find(Long id) {
return session.load(getEntityType(), id, DEPTH_ENTITY);
}
public void delete(Long id) {
session.delete(session.load(getEntityType(), id));
}
public void createOrUpdate(T entity) {
session.save(entity, DEPTH_ENTITY);
}
public abstract Class<T> getEntityType();
}
Calling code :
public static void main(String args[]) {
Hashtag hashtag = new Hashtag("fun");
HashtagService service = new HashtagServiceImpl();
service.createOrUpdate(hashtag);
}
I am running the code on eclipse as simple java process, and not on any Application server.
Here is the full log with trace:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/lazywiz/.m2/repository/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/lazywiz/.m2/repository/org/slf4j/slf4j-jdk14/1.5.11/slf4j-jdk14-1.5.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/lazywiz/.m2/repository/ch/qos/logback/logback-classic/1.1.2/logback-classic-1.1.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
15/10/09 14:55:16 INFO info.ClassFileProcessor: Starting Post-processing phase
15/10/09 14:55:16 INFO info.ClassFileProcessor: Building annotation class map
15/10/09 14:55:16 INFO info.ClassFileProcessor: Building interface class map for 9 classes
15/10/09 14:55:16 INFO info.ClassFileProcessor: Registering default type converters...
15/10/09 14:55:16 INFO info.ClassFileProcessor: Post-processing complete
15/10/09 14:55:16 INFO info.ClassFileProcessor: 9 classes loaded in 16 milliseconds
15/10/09 14:55:17 WARN request.DefaultRequest: Caught response exception: No Host
Exception in thread "main" org.neo4j.ogm.session.result.ResultProcessingException: Failed to execute request: {"statements":[{"statement":"CREATE (_0:`Hashtag`{_0_props}) RETURN id(_0) AS _0","parameters":{"_0_props":{"name":"varun"}},"resultDataContents":["row"],"includeStats":false}]}
at org.neo4j.ogm.session.request.DefaultRequest.execute(DefaultRequest.java:105)
at org.neo4j.ogm.session.request.SessionRequestHandler.execute(SessionRequestHandler.java:99)
at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:68)
at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:391)
at com.readypulse.rpinfluencernetwork.ogm.service.GenericService.createOrUpdate(GenericService.java:26)
at com.readypulse.rpinfluencernetwork.GraphManager.main(GraphManager.java:16)
Caused by: org.apache.http.client.HttpResponseException: No Host
at org.neo4j.ogm.session.request.DefaultRequest.execute(DefaultRequest.java:86)
... 5 more
Can someone please suggest where I am going wrong.
Prior to this I was having a totally different code base where I was using
graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(dbPath); and
But later I realized that this is not the right way when I want to connect to the neo4j server running in prod environment. I want to start the server and hence connect is via java and Ruby client concurrently.
Thanks!
Some points :
a)
You can not use neo4j as password, this is the default password when you install a new database, but the password need to be changed at first start.
For changing the password :
Open the Neo4j browser and the first prompt will ask you to change the password
Or issue a curl request for changing the password :
curl -H "Content-Type: application/json"\
-H "Authorization: Basic echo -n 'neo4j:neo4j' | base64"\
-X POST -d '{"password":"yourNewPassword"}'\
-I http://localhost:7474/user/neo4j/password
b) If you're not using SDN4, In the sessionFactoryyou need to pass the user and password as arguments to the openSession method:
Session session = sessionFactory.openSession("http://localhost:7474", username, password);
Docs :
Neo4j Authentication: http://neo4j.com/docs/stable/rest-api-security.html#rest-api-user-status-on-first-access
Neo4j OGM Session Authentication : http://neo4j.com/docs/ogm/java/stable/#reference_programming-model_session

Deleting a Neo4j node from a Spring application

I'm trying to delete a node from an embedded graph.
I'm using SDN 3.3.2.RELEASE and my Neo4j is 2.2.4.
But the node is still around after the repository call:
org.junit.ComparisonFailure: expected:<null> but was:<Manufacturer [name=Siemens]>
Here is my test:
#Test
public void testDeleteById() {
Neo4JManufacturer loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).exists();
neo4JManufacturerRepository.deleteManufacturer(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).doesNotExist();
}
The repository is:
public interface Neo4JManufacturerRepository extends GraphRepository<Neo4JManufacturer> {
#Transactional
#Query("start u = node({id}) match u-[r]-() delete u,r")
public void deleteManufacturer(#Param("id") Long id);
Before trying to delete with the deleteManufacturer() method, I was trying to delete with the delete() method, as in:
neo4JManufacturerRepository.delete(manufacturer0.getId());
But I would get the exact same test failure:
org.junit.ComparisonFailure: expected:<null> but was:<Manufacturer [name=Siemens]>
This is my node class:
#NodeEntity
#SequenceGenerator(name = "id_generator", sequenceName = "sq_id_manufacturer")
public class Neo4JManufacturer extends BaseEntity {
#Column(nullable = false, unique = true)
#Indexed
private String name;
public Neo4JManufacturer() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Manufacturer [name=" + name + "]";
}
}
UPDATE:
Following the two comments, I added another find call after the delete call so as to trigger a flush to the data store:
Neo4JManufacturer loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).exists();
neo4JManufacturerRepository.delete(manufacturer0.getId());
loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).doesNotExist();
And I also added the optional clause in the query:
#Transactional
#Query("START u = node({id}) OPTIONAL MATCH u-[r]-() DELETE u,r")
public void deleteManufacturer(#Param("id") Long id);
Now, I get a different error:
No primary SDN label exists .. (i.e one starting with _)
I suppose the flush after the delete is being attempted, but fails for another reason ?
Also, I wonder if the flushing and transactin behavior is the same when using JPA and when using Neo4j. Indeed the following JPA test works just as expected:
#Test
public void testDeleteByUserId() {
User loadedUser = userRepository.findOne(user0.getId());
assertThatUser(loadedUser).exists();
loadedUser = userRepository.deleteByUserId(user0.getId());
loadedUser = userRepository.findOne(user0.getId());
assertThatUser(loadedUser).doesNotExist();
}
My idea is to do the same with the Neo4j database.
UPDATE: I'm now deleting on a transactional service instead of directly on the repository:
#Modifying
#Transactional(rollbackFor = EntityNotFoundException.class)
#Override
public Neo4JManufacturer delete(Long id) throws EntityNotFoundException {
Neo4JManufacturer manufacturer = findById(id);
if (manufacturer == null) {
throw new EntityNotFoundException();
} else {
neo4jManufacturerRepository.delete(manufacturer.getId());
return manufacturer;
}
}
The service is called like:
#Test
public void testDeleteById() {
Neo4JManufacturer loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).exists();
neo4JManufacturerService.delete(manufacturer0.getId());
Neo4JManufacturer anotherManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(anotherManufacturer).doesNotExist();
}
But I still get the same exception:
testDeleteById(it.kahoot.robot.data.neo4j.Neo4JManufacturerRepositoryTest) Time elapsed: 0.137 sec <<< ERROR!
org.springframework.dao.InvalidDataAccessApiUsageException: No primary SDN label exists .. (i.e one starting with _) ; nested exception is java.lang.IllegalStateException: No primary SDN label exists .. (i.e one starting with _)
at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:136)
at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:40)
at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:36)
at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:26)
at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:102)
UPDATE II: I have now a test that passes fine and behaves as expected. Still the test is under a #Transactional annotation. And it does not do any manual flush. The interesting part, and the reason the test stopped giving the error:
No primary SDN label exists .. (i.e one starting with _)
is that the findById or findOne calls were replaced by a findByName call. Doing either a findById or findOne call would trigger the above error.
Here is how the working test looks like:
#Test
public void testIsDelete() {
Neo4JManufacturer loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).exists();
neo4JManufacturerRepository.delete(manufacturer0.getId());
loadedManufacturer = neo4JManufacturerService.findByName(loadedManufacturer.getName());
assertThatManufacturer(loadedManufacturer).doesNotExist();
}
The repository findByName is:
Neo4JManufacturer findByName(String name);
I had a logger in place which showed the id being set above 0
DEBUG [Neo4JManufacturerRepositoryTest] ==========>> Id: 4 Name: Siemens
Still, using a findById instead of a findByName was giving the above error.
And I wonder why.
UPDATE III:
I have now removed the transactional annotation from the test.
Here is the test:
#Test
public void testDelete() {
Neo4JManufacturer loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).exists();
neo4JManufacturerRepository.delete(manufacturer0.getId());
loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).doesNotExist();
assertThatManufacturer(manufacturer0).exists();
manufacturer0 = neo4JManufacturerRepository.save(manufacturer0);
}
The console says:
2016-09-01 14:45:57,769 DEBUG [main] c.t.d.n.Neo4JManufacturerRepositoryTest ==========>> Before - Created manufacturer0 - Id: 0
2016-09-01 14:45:58,127 DEBUG [main] c.t.d.n.Neo4JManufacturerRepositoryTest ==========>> After - Deleted manufacturer0 - Id: 0
2016-09-01 14:45:58,320 DEBUG [main] c.t.d.n.Neo4JManufacturerRepositoryTest ==========>> Before - Created manufacturer0 - Id: 4
2016-09-01 14:45:58,850 DEBUG [main] c.t.d.n.Neo4JManufacturerRepositoryTest ==========>> After - Deleted manufacturer0 - Id: 4
2016-09-01 14:45:59,035 DEBUG [main] c.t.d.n.Neo4JManufacturerRepositoryTest ==========>> Before - Created manufacturer0 - Id: 8
Tests run: 4, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 23.625 sec <<< FAILURE! - in com.thalasoft.data.neo4j.Neo4JManufacturerRepositoryTest
testDelete(com.thalasoft.data.neo4j.Neo4JManufacturerRepositoryTest) Time elapsed: 0.334 sec <<< ERROR!
org.springframework.dao.DataRetrievalFailureException: Node 8 not found; nested exception is org.neo4j.graphdb.NotFoundException: Node 8 not found
It errors on the last source code line of the test, the one doing the save() operation.
Why does it look for a node ? Why does it complain it is not found ? Shouldn't it be not found since I'm trying to create it ?
Since your Cypher query is using MATCH u-[r]-(), it will fail if the specified node does not participate in any relationships.
You should use OPTIONAL MATCH instead, which allows the query to succeed even if the specified node does not participate in any relationships:
"START u = node({id}) OPTIONAL MATCH u-[r]-() DELETE u,r"
Within the same transaction the node is still visible, and as your test is very probably #Transactional per method, the tx is only finished (rolled back) after the method finishes
see: http://neo4j.com/docs/stable/transactions-delete.html
If you add a transactional handling (commit) after the delete without having the #Transactional on top of your test. You should not be able to see the node anymore.
Update
I ran your test, it is as I explained.
Your test (via its superclass) had a #Transactional annotation, so that all operations within the test method are executed in one transaction and afterwards rolled back.
If you want to see the real world behavior the deletion would have to happen in it's own tx (as part of your service call) after that transaction is finished, the node is no longer visible.
Also your assert tested that the previously loaded node would be null, which is never the case because the already existing variable is not changed as part of your delete operation.
I removed the global #Transactional annotation from your test-superclass and changed the test method to this:
#Test
public void testDeleteById() {
Neo4JManufacturer loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).exists();
neo4JManufacturerService.delete(manufacturer0.getId());
loadedManufacturer = neo4JManufacturerRepository.findOne(manufacturer0.getId());
// both would work
// loadedManufacturer = neo4JManufacturerService.findById(manufacturer0.getId());
assertThatManufacturer(loadedManufacturer).doesNotExist();
}
Now it passes and exhibits the expected behavior: after the tx with the delete is completed the node is gone.

Index name for class java.lang.String name rel: false idx: true must differ from the default name (SDNĀ 3.0)

After upgrading from SDN 2.3.4 to 3.0.0.RELEASE, I have two tests that fail with :
Index name for class java.lang.String name rel: false idx: true must differ from the default name: BesoinNode
In the BesoinNode class, I previously used #Indexed(indexName = "indexOfNeeds") but removed the indexName because of https://stackoverflow.com/a/22084251/1528942. Now my index declaration is simply :
#Indexed
private String name;
The failing test code is :
#Test
public final void t02_TestBesoinDAORetrieve() {
// We create a new object in the datastore
Besoin need1 = needDAO.create(new BesoinNode("t02 need"));
assertEquals(new Long(1), needDAO.count());
// We retrieve the object from the datastore using its id
Besoin need1bis = needDAO.retrieveById(need1.getId());
assertEquals(need1, need1bis);
assertEquals(need1.getId(), need1bis.getId());
assertEquals(need1.getName(), need1bis.getName());
// We retrieve the object from the datastore using its name
Besoin need1ter = needDAO.retrieveByName(need1.getName());
assertEquals(need1, need1ter);
assertEquals(need1.getId(), need1ter.getId());
assertEquals(need1.getName(), need1ter.getName());
}
The test succeeds if I comment out the call to retrieveByName. Code is below:
#Override
public Besoin retrieveByName(String name) {
return repository.findByPropertyValue("name", name);
}
How can I rewrite my retrieveByName method to solve the error? Thanks!
Note : while this may no be related to the problem, I see in the API that IndexRepository#findByPropertyValue(String, Object) is deprecated. Only AbstractGraphRepository#findByPropertyValue(String, Object) is not deprecated. The problem is that my repository extends GraphRepository and that GraphRepository extends IndexRepository, something I have no control over.
Note 2 : Using #Indexed(indexType = IndexType.SIMPLE) makes the test succeed. But this toggles the old index system (not the new one which is label-based).

Resources