2012-11-13 12 views
5

Sto riscontrando un comportamento strano quando cerco entità nodo con Spring Data Neo4j (SDN). Se utilizzo GraphRepository.findOne (long) restituirà un'entità con quell'identificatore anche se l'entità non è dello stesso tipo.Risoluzione di entità con Spring Data Neo4j restituisce tipi di entità errati

Questo è ciò che la mia struttura entità (molto) assomiglia semplificato: repository

@NodeEntity 
protected abstract class BaseEntity { 

    @GraphId 
    private Long id; 

    @JsonIgnore 
    @RelatedTo(type = RelationType.ENTITY_AUDIT) 
    private Audit audit; 

} 

@NodeEntity 
public final class Person extends BaseEntity { 

    @Indexed(indexType = IndexType.FULLTEXT) 
    private String firstName; 

    @Indexed(indexType = IndexType.FULLTEXT) 
    private String lastName; 

} 

@NodeEntity 
public class Audit extends BaseEntity { 

    @RelatedTo(type = RelationType.ENTITY_AUDIT, direction = Direction.INCOMING) 
    private BaseEntity parent; 

    private Long date; 

    private String user; 

} 

Per ogni tipo di entità, che ho creato come questo:

@Repository 
public interface PersonRepository extends GraphRepository<Person> {} 

@Repository 
public interface AuditRepository extends GraphRepository<Audit> {} 

Ho un abstract classe base per le mie classi del livello di servizio. Questo è ciò che grosso modo simile: Quando eseguo il seguente codice, il risultato non è come previsto

public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> { 

    private GraphRepository<T> repository; 

    public MyServiceImpl(final GraphRepository<T> repository) { 
     this.repository = repository; 
    } 

    @Override 
    public T read(final Long identifier) throws EntityNotFoundException { 
     return repository.findOne(identifier); 
    } 

    @Override 
    public T create(final T entity) { 
     return repository.save(entity); 
    } 

} 

@Service 
public class PersonServiceImpl extends MyServiceImpl<Person> implements PersonService { 

    private PersonRepository personRepository; 

    @Autowired 
    public PersonServiceImpl(final PersonRepository personRepository) { 
     super(personRepository); 
     this.personRepository = personRepository; 
    } 

} 

:

Person person = new Person(); 
person.setFirstName("Test"); 
person.setLastName("Person"); 
personService.create(person); 
// suppose the person identifier is 1L 
final Audit audit = auditRepository.findOne(1L); 

Ci si aspetterebbe che l'AuditRepository sarebbe tornato nulla, ma questo in non è il caso. Invece, restituisce un Audit con identificatore 1L e null in tutte le sue proprietà. Sembra che finché c'è un nodo che corrisponde a un determinato identificatore, verrà restituito, non importa quale sia il suo tipo. Se Person e Audit avessero nomi di proprietà corrispondenti, anch'essi conterrebbero anche i loro valori ... Tutto questo comportamento previsto o mi manca qualcosa?

Per ora, ho risolto questo problema con il codice riportato di seguito, in cui eseguo personalmente il controllo di tipo.

public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> { 

    private GraphRepository<T> repository; 

    public MyServiceImpl(final GraphRepository<T> repository) { 
     this.repository = repository; 
    } 

    @Override 
    public T read(final Long identifier) throws EntityNotFoundException { 
     return get(identifier); 
    } 

    protected T get(final Long identifier) throws EntityNotFoundException {  
     final T entity = repository.findOne(identifier); 
     final Class<T> type = getServiceType(); 
     if (entity == null || !(type.equals(repository.getStoredJavaType(entity)))) { 
      throw new EntityNotFoundException(type, identifier); 
     } 
     return entity; 
    } 

    @SuppressWarnings("unchecked") 
    private Class<T> getServiceType() { 
     return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()) 
       .getActualTypeArguments()[0]; 
    } 

} 

Se è necessaria più configurazione, per favore fatemelo sapere.

mie versioni framework sono:

<spring.version>3.2.0.RC1</spring.version> 
<neo4j.version>1.8</neo4j.version> 
<spring.data.neo4j.version>2.1.0.RELEASE</spring.data.neo4j.version> 
+0

grazie @tstorms, questo post e la soluzione sono stati molto utili per me quando ho avuto a che fare con questo bug. – Hendrik

+0

Solo una breve domanda: l'annotazione '@ Autowired' di 'PersonServiceImpl' si trova nel campo' PersonRepository' piuttosto che nel costruttore? – tigerjack89

+0

No, perché? Ho scelto l'iniezione del costruttore invece dell'iniezione sul campo. Una lettura molto interessante sul perché l'iniezione sul campo può essere "cattiva": http://olivergierke.de/2013/11/why-field-injection-is-evil/. – tstorms

risposta

2

abbiamo avuto quel comportamento prima che non è riuscito sul tipo di entità sbagliato viene restituito, abbiamo cambiato che il comportamento in modo che il tipo che fornisci viene usata per proiettare automaticamente il nodo .

public <S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy) {..} 

modello. createEntityFromStoredType (node, null) otterrà l'oggetto con lo stato memorizzato.

public Class getStoredJavaType(Object entity) {} 

ti dà la classe conservati per un nodo o relazione (o ente)

Abbiamo avuto una discussione di cambiare il comportamento di nuovo e non riuscendo esp. nei repository.

La domanda è: cosa dovrebbe succedere allora? Un'eccezione? Un risultato Null? ...

In generale se si fornisce un ID nodo non valido che è valido, restituendo un errore o Null non sembra essere come una risposta corretta sia?

+0

Nella mia soluzione alternativa, lancio un'eccezione quando viene restituita un'entità di un tipo errato. Per me, questo mi sembra naturale. Forse è solo una questione di gusti? Anche se cambiare il comportamento sarebbe l'ideale per il mio progetto, non spezzerebbe molti ambienti di produzione? – tstorms

+0

Sono d'accordo che un'eccezione dovrebbe essere generata quando viene trovato un nodo con l'id, ma non è del tipo con l'eccezione. Perché? Uso annotazioni su proprietà del tipo atteso. Viene generata un'eccezione perché le proprietà previste non vengono trovate quando l'oggetto viene inizializzato. Un'eccezione dal repository sarebbe più pulita in quel caso IMHO. – spa

+1

Per soddisfare la maggior parte degli scenari di utilizzo: Forse il comportamento desiderato dovrebbe essere configurabile. – spa

Problemi correlati