6

Ho iniziato a lavorare con Spring Data Elasticsearch su Spring Boot 1.3.1 e voglio utilizzare la stessa entità che utilizza nel mio database e ha una chiave composta. Classeè possibile creare spring-data-elasticsearch @Document con la chiave composta?

Entity:

@IdClass(PassengerPk.class) 
@Table(name = "passenger") 
@Document(indexName="passenger") 
public class Passenger implements Serializable { 

    @Id 
    @ManyToOne 
    @JoinColumn(columnDefinition="long", name="user_id", referencedColumnName="id") 
    private User user; 

    @Id 
    @ManyToOne 
    @JoinColumn(columnDefinition="long", name="scheduler_id", referencedColumnName="id") 
    private Scheduler scheduler; 

    @Column(name = "is_active") 
    private Boolean isActive; 

    ... 
} 

classe Key:

public class PassengerPk implements Serializable { 

    private Long user; 
    private Long scheduler; 

    public PassengerPk() { 
    } 

    public PassengerPk(Long user, Long scheduler) { 
     this.user = user; 
     this.scheduler = scheduler; 
    } 
    ... 
} 

JPA elasticsearch repository:

public interface PassengerSearchRepository extends ElasticsearchRepository<Passenger, PassengerPk> { 

} 

Database: database relationships

Se provo a compilare questo codice, ottengo questo errore.

Caused by: java.lang.IllegalArgumentException: Unsuppored ID type class com.dualion.test.domain.PassengerPk 
    at org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactory.getRepositoryBaseClass(ElasticsearchRepositoryFactory.java:79) ~[spring-data-elasticsearch-1.3.1.RELEASE.jar:na] 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepositoryInformation(RepositoryFactorySupport.java:238) ~[spring-data-commons-1.11.1.RELEASE.jar:na] 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:181) ~[spring-data-commons-1.11.1.RELEASE.jar:na] 
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251) ~[spring-data-commons-1.11.1.RELEASE.jar:na] 
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237) ~[spring-data-commons-1.11.1.RELEASE.jar:na] 
    at org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactoryBean.afterPropertiesSet(ElasticsearchRepositoryFactoryBean.java:55) ~[spring-data-elasticsearch-1.3.1.RELEASE.jar:na] 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE] 
    ... 71 common frames omitted 

Come posso modificare il mio codice?

Grazie

+0

qualcuno può darmi una soluzione? – kn4ls

+0

Come si eredita la dipendenza da '@ IdClass'?Penso che sia una classe portata da una dipendenza JPA, ES con JPA non è così banale da configurare, non so come. Quindi ovviamente non puoi usarlo per ES. A proposito, sarebbe molto utile per me sapere come raggiungere il tuo obiettivo. – andPat

+0

Infine usa HibernateSearch e non ES, perché l'implementazione è più semplice. – kn4ls

risposta

0

avevo letto le risposte relative altrove e aveva concluso che non era possibile; tuttavia, la mia testardaggine ha avuto il meglio di me e ho trovato una soluzione.

TLDR; Forza molla per usare un nuovo repository che prende l'hashCode del tuo ID composito e usa il suo valore String come suo id.

Passi ...

creare una nuova Repository in grado di gestire gli ID compositi:

public class HashKeyedRepository<T, ID extends Serializable> extends AbstractElasticsearchRepository<T, ID> { 

    public HashKeyedRepository() { 
     super(); 
    } 

    public HashKeyedRepository(ElasticsearchEntityInformation<T, ID> metadata, 
           ElasticsearchOperations elasticsearchOperations) { 
     super(metadata, elasticsearchOperations); 
    } 

    public HashKeyedRepository(ElasticsearchOperations elasticsearchOperations) { 
     super(elasticsearchOperations); 
    } 

    @Override 
    protected String stringIdRepresentation(ID id) { 
     return String.valueOf(id.hashCode()); 
    } 
} 

Si noti che questo presuppone che sia stato implementato .hashCode sulla tua classe id composito correttamente per lavorare.

successivo è necessario creare un nuovo RepositoryFactoryBean che tornerà questa nuova Repository:

public class CustomElasticsearchRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends ElasticsearchRepositoryFactoryBean<T, S, ID> { 

    private ElasticsearchOperations operations; 

    public CustomElasticsearchRepositoryFactoryBean(Class<? extends T> repositoryInterface) { 
     super(repositoryInterface); 
    } 

    public void setElasticsearchOperations(ElasticsearchOperations operations) { 
     super.setElasticsearchOperations(operations); 
     Assert.notNull(operations);  
     this.operations = operations; 
    } 

    @Override 
    protected RepositoryFactorySupport createRepositoryFactory() { 
     return new ElasticsearchRepositoryFactory(operations) { 
      @Override 
      protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { 
       if (!Integer.class.isAssignableFrom(metadata.getIdType()) && !Long.class.isAssignableFrom(metadata.getIdType()) && !Double.class.isAssignableFrom(metadata.getIdType()) && metadata.getIdType() != String.class && metadata.getIdType() != UUID.class) { 
        return HashKeyedRepository.class; 
       } 
       return super.getRepositoryBaseClass(metadata); 
      } 
     }; 
    } 
} 

Infine, durante l'abilitazione repository, inserisci la tua nuova RepositoryFactoryBean classe:

@EnableElasticsearchRepositories(basePackages = "xxx.xxx.repository.search", repositoryFactoryBeanClass = CustomElasticsearchRepositoryFactoryBean.class) 

Questa implementazione ricade al valore predefinito se viene utilizzato uno qualsiasi degli ID supportati al momento della scrittura (cioè String, UUID, Number). Non so se è una soluzione GRANDE dato che ci sono possibilità di conflitto con .hashCode ma sta funzionando per me adesso.

PS sto usando lombok s @Data di auto generare .hashCode per me.

PPS Un'altra soluzione che ho visto menzionare altri (non java) è codificare in base64 una versione serializzata dell'ID (cioè JSON). Penso che questo non garantirebbe conflitti, ma dovresti essere sicuro di eliminare qualsiasi carattere in eccesso (ad esempio spazi bianchi) e garantire l'ordine delle proprietà affinché sia ​​efficace.