Spring Data Neo4j - repository.save e @Indexed (exclusivo = verdadeiro)

Hoje eu tentei o Spring Data Neo4j, finalmente consegui funcionarde alguma forma...

Estou a usar:

Primavera 4.0.2Spring Data Neo4j 3.0.0QueryDSL 3.3.1Neo4j 2.0.1

Aqui está a minha configuração:

@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");
    }

}

Minha classe de domínio:

@NodeEntity
@QueryEntity
public class User implements Persistable<Long> {

    @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;
    }

}

E meu Repositório:

interface UserRepository extends GraphRepository<User>, CypherDslRepository<User> {}

Posso criar com êxito um usuário no banco de dados e depois recuperá-lo via:

User u = repo.query( start(allNodes("user")) .where(toBooleanExpression(QUser.user.email.eq("[email protected]"))) .returns(node("user")), new HashMap<String, Object>()) .singleOrNull();

MAS: se eu chamar meu código de criação uma segunda vez,não lançar uma exceção por causa de@Indexed(unique=true) String email, apenas substitui o objeto no banco de dados.

E: se eu tentar criar um segundoUser com um valor de email diferente, o usuário antigo é substituído.

O código de criação é tão simples quanto:

User u = new User();
u.setEmail("[email protected]");
repo.save(u);

Eu também tentei usar uma versão autônoma do Neo4j em vez da incorporada, com exatamente o mesmo resultado. Na visualização webadmin, vejo que ele criou alguns índices:

Node Indexes:                 Relationship Indexes:

User     {"type":"exact"}     __rel_types__   {"type":"exact"}
lucene                        lucene

A saída de depuração também me diz que o Spring cria um índice:

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 {}

Um pouco mais de saída de depuração:

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"
} ]

Eu realmente não consigo imaginar o que estou fazendo de errado aqui ...

Por favor me ajude!

ATUALIZAÇÃO # 1:

Pelo que eu vi emAbstractGraphRepository.save usaNeo4jTemplate.save que diz:

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.

Portanto, presumo que ele sempre "pense" que minha entidade já está anexada. Masporque?

ATUALIZAÇÃO # 2:

Se eu for ao webadmin e fizer simplesmente duas vezes:

CREATE (n:User {email:'[email protected]'})

Eu recebo um erro. Portanto, deve haver algo errado com meu código Java ou SDN ...

ATUALIZAÇÃO # 3:

Spring Data Neo4j'ssave O método faz algo comoGET ou CREATE:

User u1 = new User();
u1.setEmail("[email protected]");
repo.save(u1); // creates node with id=0

User u2 = new User();
u2.setEmail("[email protected]");
repo.save(u2); // creates node with id=1

User u3 = new User();
u3.setEmail("[email protected]");
repo.save(u3); // updates and returns node with id=0

Como posso corrigir esse comportamento? Eu quero uma exceção.

ATUALIZAÇÃO # 4:

Parece que eu estava procurando por isso:http://docs.neo4j.org/chunked/stable/rest-api-unique-indexes.html#rest-api-create-a-unique-node-or-return-fail-create

Map<String, Object> prop1 = new HashMap<String, Object>();
prop1.put("email", "[email protected]");
neo4jTemplate.createNodeAs(User.class, prop1);

Map<String, Object> prop2 = new HashMap<String, Object>();
prop2.put("email", "[email protected]");
neo4jTemplate.createNodeAs(User.class, prop2);

Dessa forma, funciona como esperado, pelo menos eu recebo uma exceção:

org.neo4j.rest.graphdb.RestResultException: Node 7 already exists with label User and property "email"=[[email protected]]

Mas agora não consigo descobrir como integrar isso ao Spring Data Repository ...

questionAnswers(1)

yourAnswerToTheQuestion