title: Spring Data Neo4j - repository.save and @Indexed(unique=true)
body: <p>Today I tried Spring Data Neo4j, I finally got it working <strong>somehow</strong>...</p>
  
  <p>I'm using:</p>
  
  <ul>
  <li>Spring 4.0.2</li>
  <li>Spring Data Neo4j 3.0.0</li>
  <li>QueryDSL 3.3.1</li>
  <li>Neo4j 2.0.1</li>
  </ul>
  
  <p>Here's my config:</p>
  
  <pre><code>@Configuration
  @EnableNeo4jRepositories(includeFilters=@Filter(value=GraphRepository.class, type=FilterType.ASSIGNABLE_TYPE))
  public class Neo4jConfig extends Neo4jConfiguration {
  
      public Neo4jConfig() {
          setBasePackage("my.base.package");
      }
  
      @Bean
      public GraphDatabaseService graphDatabaseService() {
          return new GraphDatabaseFactory().newEmbeddedDatabase("/tmp/neo4j");
      }
  
  }
  </code></pre>
  
  <p>My Domain Class:</p>
  
  <pre><code>@NodeEntity
  @QueryEntity
  public class User implements Persistable&lt;Long&gt; {
  
      @GraphId private Long id;
      public Long getId() { return id; }
  
      @NotNull @NotBlank @Email
      @Indexed(unique=true)
      private String email;
      public String getEmail() { return email; }
      void setEmail(String email) { this.email = email; }
  
      @Override
      public boolean isNew() {
          return id==null;
      }
  
      @Override
      public int hashCode() {
          return id == null ? System.identityHashCode(this) : id.hashCode();
      }
      @Override
      public boolean equals(Object obj) {
          if (this == obj)
              return true;
          if (obj == null)
              return false;
          if (getClass() != obj.getClass())
              return false;
          User other = (User) obj;
          if (id == null) {
              if (other.id != null)
                  return false;
          } else if (!id.equals(other.id))
              return false;
          return true;
      }
  
  }
  </code></pre>
  
  <p>And my Repository:</p>
  
  <pre><code>interface UserRepository extends GraphRepository&lt;User&gt;, CypherDslRepository&lt;User&gt; {}
  </code></pre>
  
  <hr>
  
  <p>I can successfully create a User in DB and afterwards retrieve it via:</p>
  
  <pre><code>User u = repo.query(
      start(allNodes("user"))
          .where(toBooleanExpression(QUser.user.email.eq("my@email.com")))
          .returns(node("user")), new HashMap&lt;String, Object&gt;())
      .singleOrNull();
  </code></pre>
  
  <p><strong>BUT:</strong> if I now call my create code a second time, it <strong>won't</strong> throw an exception because of <code>@Indexed(unique=true) String email</code>, it just overrides the the object in the DB.</p>
  
  <p><strong>AND:</strong> if I try to create a second <code>User</code> with a different email value, the old user get's overridden.</p>
  
  <p>The create code is just as simple as:</p>
  
  <pre><code>User u = new User();
  u.setEmail("some@email-address.com");
  repo.save(u);
  </code></pre>
  
  <p>I also tried to use a standalone version of Neo4j instead of the embedded one, with exactly the same result. In the webadmin view I can see, that it created some Indexes:</p>
  
  <pre><code>Node Indexes:                 Relationship Indexes:
  
  User     {"type":"exact"}     __rel_types__   {"type":"exact"}
  lucene                        lucene
  </code></pre>
  
  <p>The debug output also tells me that Spring creates an index:</p>
  
  <pre><code>2014-03-12 21:00:34,176 DEBUG  o.s.data.neo4j.support.schema.SchemaIndexProvider:  35 - CREATE CONSTRAINT ON (n:`User`) ASSERT n.`email` IS UNIQUE
  2014-03-12 21:00:34,177 DEBUG     o.s.data.neo4j.support.query.CypherQueryEngine:  63 - Executing cypher query: CREATE CONSTRAINT ON (n:`User`) ASSERT n.`email` IS UNIQUE params {}
  </code></pre>
  
  <p>Some more debug output:</p>
  
  <pre><code>curl -v http://localhost:7474/db/data/index/node
  
  {
    "User" : {
      "template" : "http://localhost:7474/db/data/index/node/User/{key}/{value}",
      "provider" : "lucene",
      "type" : "exact"
  }
  
  
  curl -v http://localhost:7474/db/data/schema/index
  
  [ {
    "property_keys" : [ "email" ],
    "label" : "User"
  } ]
  
  
  curl -v http://localhost:7474/db/data/schema/constraint
  
  [ {
    "property_keys" : [ "email" ],
    "label" : "User",
    "type" : "UNIQUENESS"
  } ]
  </code></pre>
  
  <p>I really can't imagine what I am doing wrong here... </p>
  
  <p>Please help me! </p>
  
  <hr>
  
  <p><strong>UPDATE #1</strong>:</p>
  
  <p>From what I've seen in <code>AbstractGraphRepository.save</code> it uses <code>Neo4jTemplate.save</code> which says:</p>
  
  <pre><code>Stores the given entity in the graph, if the entity is already attached to the graph, the node is updated, otherwise a new node is created.
  </code></pre>
  
  <p>So I assume that it always "thinks" that my entity is already attached. But <strong>why</strong>?</p>
  
  <hr>
  
  <p><strong>UPDATE #2</strong>:</p>
  
  <p>If I go to the webadmin and do simply twice:</p>
  
  <pre><code>CREATE (n:User {email:'test@mail.com'})
  </code></pre>
  
  <p>I get an error. So there must be something wrong with my Java code or SDN...</p>
  
  <hr>
  
  <p><strong>UPDATE #3</strong>:</p>
  
  <p>Spring Data Neo4j's <code>save</code> Method does something like <em>GET or CREATE</em>:</p>
  
  <pre><code>User u1 = new User();
  u1.setEmail("a@email.com");
  repo.save(u1); // creates node with id=0
  
  User u2 = new User();
  u2.setEmail("b@email.com");
  repo.save(u2); // creates node with id=1
  
  User u3 = new User();
  u3.setEmail("a@email.com");
  repo.save(u3); // updates and returns node with id=0
  </code></pre>
  
  <p>How can I fix this behavior? I want an exception.</p>
  
  <hr>
  
  <p><strong>UPDATE #4</strong>:</p>
  
  <p>Seems like I was looking for that: <a href="http://docs.neo4j.org/chunked/stable/rest-api-unique-indexes.html#rest-api-create-a-unique-node-or-return-fail-create" rel="nofollow">http://docs.neo4j.org/chunked/stable/rest-api-unique-indexes.html#rest-api-create-a-unique-node-or-return-fail-create</a></p>
  
  <pre><code>Map&lt;String, Object&gt; prop1 = new HashMap&lt;String, Object&gt;();
  prop1.put("email", "abc@mail.com");
  neo4jTemplate.createNodeAs(User.class, prop1);
  
  Map&lt;String, Object&gt; prop2 = new HashMap&lt;String, Object&gt;();
  prop2.put("email", "abc@mail.com");
  neo4jTemplate.createNodeAs(User.class, prop2);
  </code></pre>
  
  <p>This way it works as expected, at least I get an exception: </p>
  
  <pre><code>org.neo4j.rest.graphdb.RestResultException: Node 7 already exists with label User and property "email"=[abc@mail.com]
  </code></pre>
  
  <p>But now I can't figure out how to integrate this with the Spring Data Repository...</p>