使用 Spring-data-cassandra 查询具有复合主键的表

Posted

技术标签:

【中文标题】使用 Spring-data-cassandra 查询具有复合主键的表【英文标题】:Querying Tables with Composite Primary Keys using Spring-data-cassandra 【发布时间】:2016-03-25 14:51:02 【问题描述】:

我在使用 CassandraReporitory 的 findOne() 或 findAll() 方法时遇到以下异常:

Caused by: java.lang.IllegalStateException: property does not have a single column mapping
    at org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty.getColumnName(BasicCassandraPersistentProperty.java:134)
    at org.springframework.data.cassandra.convert.BasicCassandraRowValueProvider.getPropertyValue(BasicCassandraRowValueProvider.java:64)
    at org.springframework.data.cassandra.convert.BasicCassandraRowValueProvider.getPropertyValue(BasicCassandraRowValueProvider.java:35)
    at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:78)
    at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:71)
    at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:83)
    at org.springframework.data.cassandra.convert.MappingCassandraConverter.readEntityFromRow(MappingCassandraConverter.java:133)
    at org.springframework.data.cassandra.convert.MappingCassandraConverter.readRow(MappingCassandraConverter.java:115)
    at org.springframework.data.cassandra.convert.MappingCassandraConverter.read(MappingCassandraConverter.java:200)
    at org.springframework.data.cassandra.repository.query.AbstractCassandraQuery.getSingleEntity(AbstractCassandraQuery.java:176)
    at org.springframework.data.cassandra.repository.query.AbstractCassandraQuery.execute(AbstractCassandraQuery.java:133)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:454)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:432)

我的类定义如下:

@PrimaryKeyClass
public class ItemAndLocationKey implements Serializable 

    private static final long serialVersionUID = 1L;

    @PrimaryKeyColumn(name = "itemId", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
    @CassandraType(type = Name.UUID)
    private UUID itemId;
    @PrimaryKeyColumn(name = "locationId", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
    @CassandraType(type = Name.UUID)
    private UUID locationId;

    public ItemAndLocationKey(UUID itemId, UUID locationId) 
        this.itemId = itemId;
        this.locationId = locationId;
    

    public UUID getItemId() 
        return itemId;
    

    public void setItemId(UUID itemId) 
        this.itemId = itemId;
    

    public UUID getLocationId() 
        return locationId;
    

    public void setLocationId(UUID locationId) 
        this.locationId = locationId;
    

    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ItemAndLocationKey that = (ItemAndLocationKey) o;

        if (!itemId.equals(that.itemId)) return false;
        return locationId.equals(that.locationId);
    

    @Override
    public int hashCode() 
        final int prime = 31;
        int result = 1;
        result = prime * result + itemId.hashCode();
        result = prime * result + locationId.hashCode();
        return result;
    


@Table(value = ItemAndLocation.tableName)
public class ItemAndLocation 

    @org.springframework.data.annotation.Transient
    public static final String tableName = "ItemAndLocation";

    @PrimaryKey
    private ItemAndLocationKey itemAndLocationKey;

    public ItemAndLocation(ItemAndLocationKey itemAndLocationKey) 
        this.itemAndLocationKey = itemAndLocationKey;
    

    public ItemAndLocationKey getItemAndLocationKey() 
        return itemAndLocationKey;
    

    public void setItemAndLocationKey(ItemAndLocationKey itemAndLocationKey) 
        this.itemAndLocationKey = itemAndLocationKey;
    


public interface ItemAndLocationRepository extends TypedIdCassandraRepository<ItemAndLocation, ItemAndLocationKey> 



基本上这里发生的是,在调用 findOne() 或 findAll() 期间从 Cassandra 读取一行后,spring data cassandra 尝试使用 ClassGeneratingEntityInstantiator 实例化不知道如何填充复合主的 ItemAndLocation 对象关键对象,ItemAndLocationKey。我不知道如何提供自定义对象实例化器。当前版本没有示例或文档。

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

我也遇到了同样的问题,问题出在用@Table 注释的类中。

在您的场景中,您必须删除以下类中的重载构造函数:

@Table(value = ItemAndLocation.tableName)
public class ItemAndLocation 

    @org.springframework.data.annotation.Transient
    public static final String tableName = "ItemAndLocation";

    @PrimaryKey
    private ItemAndLocationKey itemAndLocationKey;

    // \\\\\\\\\\\\\\\\\\\\ REMOVE THIS CONSTRUCTOR //////////////////
    // public ItemAndLocation(ItemAndLocationKey itemAndLocationKey) 
    //    this.itemAndLocationKey = itemAndLocationKey;
    // 
    // ////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


    public ItemAndLocationKey getItemAndLocationKey() 
        return itemAndLocationKey;
    

    public void setItemAndLocationKey(ItemAndLocationKey itemAndLocationKey) 
        this.itemAndLocationKey = itemAndLocationKey;
   

我希望这会有所帮助:)

【讨论】:

【参考方案2】:

我在 Main 实体类和 PrimaryKey-class 中都遇到了同样的问题。为我解决的问题是提供不带参数的默认构造函数并将所有字段初始化为 some 默认值,这意味着null 用于Objects,false 用于booleans 和@ 987654326@ 对于所有其他原始类型。

在您的情况下,这意味着 添加

private ItemAndLocation() 
    this.itemAndLocationKey = null;

private ItemAndLocationKey() 
    this.itemId = null;
    this.locationId = null;

构造函数不需要必须是public,字段可以是final,框架会通过反射设置值。

【讨论】:

以上是关于使用 Spring-data-cassandra 查询具有复合主键的表的主要内容,如果未能解决你的问题,请参考以下文章

spring-data-cassandra 存储库的多个键空间支持?

卡桑德拉+弹簧数据

Spring Data Cassandra 计数器更新

Spring Data Cassandra 中的分页和排序查询

Spring boot 服务启动报错 java.lang.NoSuchFieldError: INSTANCE

引起:com.datastax.driver.core.exceptions.InvalidQueryException:预计日期为 8 或 0 字节长 (13)