I'm starting to work with Neo4j and spring data.
I can do a get on the data base but can not make a set.
When I try to use the save method happens this error. It seems to be because of the unrecognized field by jackson. I looked for solutions but nothing worked.
Can anybody help me?
10:52:49.481 [http-nio-8080-exec-2] INFO o.s.d.n.config.Neo4jConfiguration - Initialising Neo4jSession
10:52:51.617 [http-nio-8080-exec-2] ERROR o.n.o.s.response.RowModelResponse - failed to parse: {"row":[1],"meta":[null]}]}
10:52:51.694 [http-nio-8080-exec-2] INFO o.s.d.n.config.Neo4jConfiguration - Intercepted exception
May 13, 2016 10:52:56 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [DispatcherServlet] in context with path [/ProjetoExemplo] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "meta" (class org.neo4j.ogm.session.result.RowModelResult), not marked as ignorable (one known property: "row"])
at [Source: {"row":[1],"meta":[null]}]}; line: 1, column: 20] (through reference chain: org.neo4j.ogm.session.result.RowModelResult["meta"])] with root cause
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "meta" (class org.neo4j.ogm.session.result.RowModelResult), not marked as ignorable (one known property: "row"])
at [Source: {"row":[1],"meta":[null]}]}; line: 1, column: 20] (through reference chain: org.neo4j.ogm.session.result.RowModelResult["meta"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:731)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:915)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1292)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1270)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3051)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2146)
at org.neo4j.ogm.session.response.RowModelResponse.next(RowModelResponse.java:45)
at org.neo4j.ogm.session.response.SessionResponseHandler.updateObjects(SessionResponseHandler.java:93)
at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:69)
at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:43)
at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:386)
My class:
#NodeEntity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Teste {
#JsonProperty("id")
private Long id;
public Long getId() {
return id;
}
private String name;
public Teste(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Teste other = (Teste) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
My call:
Teste teste1 = new Teste();
teste1.setName("TESTE");
testeService.save(teste1);
Looks like you're using Neo4j 3? If so, the version of SDN you're using is probably incompatible.
Neo4j 3.0 is compatible with these:
Current snapshot of SDN 4.2: 4.2.0.BUILD-SNAPSHOT
SDN 4.1.1: 4.1.1.RELEASE with neo4j-ogm 2.0.2
Related
I have a problem with Spring Data Neo4j and OGM. When I create a node for first time, it`s ok but if I refresh that page I get this error:
Cypher execution failed with code 'Neo.ClientError.Schema.ConstraintValidationFailed': Node(126) already exists with label Country and property name = 'Country-1'.
I searched the web and read so many documents about equals and hashCode but none of them is helping. Here are my classes:
public abstract class Place {
#Id
#GeneratedValue(strategy = UuidStrategy.class)
private String id ;
private String name ;
public String getId(){return id ;}
}
#Getter
#Setter
#NodeEntity(label = "Country")
#CompositeIndex(unique = true , properties = "name")
public class Country extends Place {
private String label = "Country";
public Country() {}
#Override
public int hashCode() {
int result = label == null ? 1 : label.hashCode();
result = 31 * result + this.getName() == null ? 0 : this.getName().hashCode();
result = 31 * result + this.getId() == null ? 0 : this.getId().hashCode();
return result;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Country)) {
return false;
}
Country that = (Country) o ;
return this.getName().equals(that.getName())
&& this.getId().equals(that.getId())
&& this.getLabel().equals(that.getLabel());
}
}
Repository is default. As I know it`s a problem of equality check but how can I fix this?
You created a constraint on your Country entity by defining
#CompositeIndex(unique = true , properties = "name")
and probably you also enabled the auto index manager feature in the Neo4j-OGM SessionFactory configuration.
This is not related to any implementation of hashCode or equals.
This will also explain the behaviour you are facing: First run succeeds but the very same action repeated failed.
I am trying to create extension procedure to Neo4j that will return complex Object (mean object that conation another object).
public static class A {
public final String a;
public A(String a) {
this.a = a;
}
}
public static class Output {
public Object out;
public Output(Object out) {
this.out = out;
}
}
#Procedure(value = "my_proc", mode = Mode.READ)
public Stream<Output> myProc() {
return Stream.of(new Output(new A("a")));
}
When I execute call my_proc(); using Neo4j Browser I it just show the progress circle and never return.
When I execute the same using Java driver, I am getting the following exception:
SEVERE: [0xedd70cbd] Fatal error occurred in the pipeline
org.neo4j.driver.internal.shaded.io.netty.handler.codec.DecoderException: Failed to read inbound message:
at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:87)
at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:35)
....
at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:645)
at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:580)
at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:497)
at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:459)
at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IndexOutOfBoundsException: readerIndex(49) + length(1) exceeds writerIndex(49): PooledDuplicatedByteBuf(ridx: 49, widx: 49, cap: 133, unwrapped: PooledUnsafeDirectByteBuf(ridx: 51, widx: 89, cap: 133))
at org.neo4j.driver.internal.shaded.io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1401)
at org.neo4j.driver.internal.shaded.io.netty.buffer.AbstractByteBuf.readByte(AbstractByteBuf.java:707)
at org.neo4j.driver.internal.async.inbound.ByteBufInput.readByte(ByteBufInput.java:45)
at org.neo4j.driver.internal.packstream.PackStream$Unpacker.unpackLong(PackStream.java:479)
at org.neo4j.driver.internal.messaging.PackStreamMessageFormatV1$Reader.unpackValue(PackStreamMessageFormatV1.java:479)
at org.neo4j.driver.internal.messaging.PackStreamMessageFormatV1$Reader.unpackRecordMessage(PackStreamMessageFormatV1.java:464)
at org.neo4j.driver.internal.messaging.PackStreamMessageFormatV1$Reader.read(PackStreamMessageFormatV1.java:390)
at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:83)
... 39 more
Is there any way to return nested object without serialize it to json before return it?
#Procedure(value = "ebc.neo4j.justamap", mode = Mode.READ)
public Stream<MapResult> justamap() {
HashMap<String, Object> v1Map = new HashMap<String, Object>(1);
HashMap<String, Object> v2Map = new HashMap<String, Object>(1);
v2Map.put("a", "a string");
v1Map.put("map inside", v2Map);
return Stream.of(new MapResult(v1Map));
}
public static class MapResult {
public Map internalmap;
public MapResult(Map aInternalId) {
this.internalmap = aInternalId;
}
}
Something like the above is possible (as deep as you want) but returning custom objects to be used in Cypher (which is the idea when you're writing a procedure) is not going to work as Cypher has no idea what those custom objects are. So you will have to do some sanitizing.
Hope this helps.
Regards,
Tom
I have a problem with using JPA annotated class in Grails (tried on grails-3.1.11 and grails-3.2.0) in my test application.
I followed description in Grails documentation mappingWithHibernateAnnotation and multiple answers on Stackoverflow for similar problems (question 1, question 2 and question 3), but no luck.
Here is a description what I do (same on both versions of Grails):
1. Create a new Grails project (I'm using IntelliJ Idea 2016.2.4).
2. Set up a datasource in application.yml:
dataSource:
configClass: org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration.class
pooled: true
jmxExport: true
dialect: org.hibernate.dialect.PostgreSQLDialect
driverClassName: org.postgresql.Driver
username: postgres
password: masterkey
3. Add PostgreSQL JDBC dependency to build.grandle:
compile group: 'org.postgresql', name: 'postgresql', version: '9.4-1200-jdbc41'
4. Create a new class DictionaryEntity.groovy in src/main/groovy:
package persistence.postgresql.mapping
import javax.persistence.*;
#Entity(name = "persistence.postgresql.mapping.DictionaryEntity")
#Table(name = "dictionary")
public class DictionaryEntity implements Serializable {
private int id;
private int word;
private int language;
private String txt;
#SequenceGenerator(name = "dictionary_id_seq_gen", sequenceName = "dictionary_id_seq", allocationSize = 1)
#Id
#GeneratedValue(generator = "dictionary_id_seq_gen", strategy = GenerationType.SEQUENCE)
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "word", nullable = false)
public int getWord() {
return word;
}
public void setWord(int word) {
this.word = word;
}
#Column(name = "language", nullable = false)
public int getLanguage() {
return language;
}
public void setLanguage(int language) {
this.language = language;
}
#Column(name = "txt", length = 128)
public String getTxt() {
return txt;
}
public void setTxt(String txt) {
this.txt = txt;
}
public DictionaryEntity() {
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DictionaryEntity that = (DictionaryEntity) o;
if (id != that.id) return false;
if (word != that.word) return false;
if (language != that.language) return false;
if (txt != null ? !txt.equals(that.txt) : that.txt != null) return false;
return true;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + word;
result = 31 * result + language;
result = 31 * result + (txt != null ? txt.hashCode() : 0);
return result;
}
}
5. Create hibernate.cfg.xml in grails-app/conf directory (tried grails-app/conf/hibernate as well):
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping class="persistence.postgresql.mapping.DictionaryEntity"/>
</session-factory>
</hibernate-configuration>
6. Created a new DictionaryEntityController in grails-app/controllers/grailsproj directory (scaffolding plugin has included to dependencies):
package grailsproj
import persistence.postgresql.mapping.DictionaryEntity
class DictionaryEntityController {
static scaffold = DictionaryEntity
}
When I'm running the application there is no domain classes ("Domains: 0" in Artefacts). If I go to my controller ("/dictionaryEntity/index") I'm getting the following error:
ERROR org.grails.web.errors.GrailsExceptionResolver - MissingMethodException occurred when processing request: [GET] /dictionaryEntity/index
No signature of method: static persistence.postgresql.mapping.DictionaryEntity.count() is applicable for argument types: () values: []
Possible solutions: print(java.lang.Object), print(java.io.PrintWriter), wait(), find(), collect(), any(). Stacktrace follows:
java.lang.reflect.InvocationTargetException: null
at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:210)
at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187)
at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
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: groovy.lang.MissingMethodException: No signature of method: static persistence.postgresql.mapping.DictionaryEntity.count() is applicable for argument types: () values: []
Possible solutions: print(java.lang.Object), print(java.io.PrintWriter), wait(), find(), collect(), any()
at grails.rest.RestfulController.countResources(RestfulController.groovy:277)
at grails.rest.RestfulController.index(RestfulController.groovy:64)
at grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
... 14 common frames omitted
Looks like DictionaryEntity hasn't recognized as domain class.
My pure Java+Hibernate application works fine with the same mapping class so I need your advice how to resolve this issue.
Update
Added annotation #grails.gorm.Entity regarding to Joshua's answer, the error has changed to:
Either class [persistence.postgresql.mapping.DictionaryEntity] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.
GORM 5 does not seem to support JPA with hibernate.cfg.xml. Take a look at this pull request conversation for more details. The main grails docs seem like they are incorrect and need to be updated
https://github.com/grails/grails-data-mapping/pull/678
also consider #grails.gorm.Entity if you control the source
this test fails with error and I don't understand why..
I thought that UniqueNodeFactory create node only if it's not exist.
Of course I can do the same using Cypher but I want to understand what happens here..
Could somebody explain?
I am using neo4j 2.3.1.
public class SimpleTest {
private GraphDatabaseService graphService;
#Before
public void setUp() throws Exception {
graphService = new TestGraphDatabaseFactory().newImpermanentDatabase();
graphService.execute("CREATE CONSTRAINT ON (user:User) ASSERT user.userId IS UNIQUE");
}
#After
public void tearDown() throws Exception {
graphService.shutdown();
}
public static UniqueFactory.UniqueNodeFactory createUserFactory(GraphDatabaseService graphDatabaseService) {
return new UniqueFactory.UniqueNodeFactory(graphDatabaseService, "User") {
#Override
protected void initialize(Node created, Map<String, Object> properties) {
created.addLabel(DynamicLabel.label("User"));
created.setProperty("userId", properties.get("userId"));
}
};
}
#Test
public void testCreateUser() throws Exception {
try (Transaction tx = graphService.beginTx()) {
Node node = graphService.createNode(DynamicLabel.label("User"));
node.setProperty("userId", 100L);
tx.success();
}
try (Transaction tx = graphService.beginTx()) {
UniqueFactory.UniqueNodeFactory uniqueFactory = createUserFactory(graphService);
uniqueFactory.getOrCreate("userId", 100L);
tx.success();
}
}
}
error :
Caused by: org.neo4j.kernel.api.exceptions.schema.UniquePropertyConstraintViolationKernelException: Node 0 already exists with label 0 and property 0=100
at org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations.validateNoExistingNodeWithLabelAndProperty(ConstraintEnforcingEntityOperations.java:165)
at org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations.nodeSetProperty(ConstraintEnforcingEntityOperations.java:140)
at org.neo4j.kernel.impl.api.LockingStatementOperations.nodeSetProperty(LockingStatementOperations.java:453)
at org.neo4j.kernel.impl.api.OperationsFacade.nodeSetProperty(OperationsFacade.java:896)
at org.neo4j.kernel.impl.core.NodeProxy.setProperty(NodeProxy.java:293)
... 33 more
Ok, now I see the reason. I created schema index with Cypher and UniqueNodeFactory use legacy index. To fix the problem you need to create nodes only using UniqueNodeFactory.
The scenario of the problem is this
1) We map the struts field values to the dtos. The dtos contain integer fields which again are displayed on the screen.
2) Now I enter an incorrect value which gives conversion error for that integer field.
3) At that point in time I decide to quit the page(i.e press cancel), I get a conversion error. This is because the StrutsConversionErrorInterceptor gets called everytime.
Is there any way that I can skip the strutsConversionErrorInterceptor when I am calling a particular method the way we can skip validation using excludeMethods
Use this code to override Struts's StrutsConversionErrorInterceptor...
public class MyConversionErrorInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";
protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
ValueStack stack = invocation.getStack();
try {
stack.push(value);
return "'" + stack.findValue("top", String.class) + "'";
} finally {
stack.pop();
}
}
#Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
BaseAction baseAction = (BaseAction) invocation.getAction();
String buttonName = baseAction.getButtonName();
for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
if (shouldAddError(propertyName, value)) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(buttonName.equalsIgnoreCas("Next")){
va.addFieldError(propertyName, message);
}
}
if (fakie == null) {
fakie = new HashMap<Object, Object>();
}
if(buttonName.equalsIgnoreCas("Next")){
fakie.put(propertyName, getOverrideExpr(invocation, value));
}
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in
// place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
protected boolean shouldAddError(String propertyName, Object value) {
if (value == null) {
return false;
}
if ("".equals(value)) {
return false;
}
if (value instanceof String[]) {
String[] array = (String[]) value;
if (array.length == 0) {
return false;
}
if (array.length > 1) {
return true;
}
String str = array[0];
if ("".equals(str)) {
return false;
}
}
return true;
}
}
You can specify you button names on which you want validation to fire. In above code I have used "Next" in code you can see
if(buttonName.equalsIgnoreCas("Next"))
Yes, you can skip calling the interceptor.
Just remove the interceptor definition from your action definition in struts.xml file.
i.e., remove <interceptor-ref name="conversionError"/>
Mainly this interceptor adds any error found in the ActionContext's conversionErrors map as a field error (provided that the action implements ValidationAware). In addition, any field that contains a validation error has its original value saved such that any subsequent requests for that value return the original value rather than the value in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to the user).
After you removed this interceptor, if the struts failed to map the field with parameter of the object(i.e., from string to int), it throws result input action error.
This seems to be a better method to handle this scenario - using Conversion Validator. Repopulating Field upon conversion Error section is something very useful:
http://struts.apache.org/2.0.14/docs/conversion-validator.html