java - 可以使用复合键制作 spring-data-elasticsearch @Document 吗?

标签 java spring elasticsearch jhipster spring-data-elasticsearch

我开始在 Spring Boot 1.3.1 上使用 Spring Data Elasticsearch,我想使用在我的数据库中使用的相同实体,它有复合键。

实体类:

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

    ...
}

关键类:

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 存储库:

public interface PassengerSearchRepository extends ElasticsearchRepository<Passenger, PassengerPk> {

}

数据库: database relationships

如果我尝试编译这段代码,我会得到这个错误。

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

如何修改我的代码?

谢谢

最佳答案

我在其他地方阅读过相关答案,并断定这是不可能的;然而,我的固执打败了我,我想出了一个解决办法。

TLDR;强制 spring 使用一个新的存储库,该存储库采用复合 id 的 hashCode 并将其 String 值用作其 id。

步骤...

创建一个可以处理复合 id 的新 Repository:

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());
    }
}

请注意,这假设您已在复合 ID 类上正确实现 .hashCode 以正常工作。

接下来,您必须创建一个新的 RepositoryFactoryBean,它将返回这个新的 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);
            }
        };
    }
}

最后,在启用存储库时,指定新的 RepositoryFactoryBean 类:

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

如果使用撰写本文时支持的任何 ID(即字符串、UUID、数字),则此实现将回退到默认值。我不知道它是否是一个很好的解决方案,因为它可能与 .hashCode 发生冲突,但它现在对我有用。

PS 我正在使用 lombok@Data 为我自动生成 .hashCode。

PPS 我看到其他人(非 java)提到的另一种解决方案是对 id(即 JSON)的序列化版本进行 base64 编码。我认为这可以保证不会发生冲突,但您必须确保去除任何多余的字符(即空格)并保证属性的顺序才能使其有效。

关于java - 可以使用复合键制作 spring-data-elasticsearch @Document 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34469968/

相关文章:

java - 比较 Java 中的字符串 .equals()

java - 如何组合列表中的其他项目来为列表元素中的项目创建子集?

Java IDE - 查找函数或类的所有间接用法/引用?

java - 设置Spring Boot可执行jar的工作目录

java - Spring重定向到web.xml配置中的另一个url?

java - 使用 Spring JDBCTemplate 更新结果集

docker - Docker上的带有Kibana的Elastic表示Kibana服务器尚未准备好

c# - 如何使用应存在的2个字段编写Nest查询

java - Android map 应用程序未在 Eclipse 上运行

sorting - Elasticsearch:按字母顺序对西类牙双名进行排序