【问题标题】:Codec not found for requested operation: [map<varchar, int> <-> java.util.Map]; issue Apache Cassandra未找到请求操作的编解码器:[map<varchar, int> <-> java.util.Map];发布 Apache Cassandra
【发布时间】:2020-10-22 22:03:36
【问题描述】:

我有一个包含字段的表格

CREATE TABLE app_category_agg (
    category text,
    app_count int,
    sp_count int,
    subscriber_count int,
    window_revenue bigint,
    top_apps frozen <list<map<text,int>>>,
    PRIMARY KEY (category)
);

当我尝试将其映射到 kotlin 模型时

@Table("app_category_agg")
class AppCategoryAggData {

    @PrimaryKeyColumn(name = "category", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
    lateinit var category: String

    @Column("app_count")
    var appCount: Int = 0

    @Column("sp_count")
    var spCount: Int = 0

    @Column("subscriber_count")
    var subscriberCount: Int = 0

    @Column("window_revenue")
    var windowRevenue: Long = 0

    @Column("top_apps")
    var topApps: List<Any> = arrayListOf()


}

interface AppCategoryAggRepository: CassandraRepository<AppCategoryAggData, String> {

    @Query(value = "SELECT * FROM analytics_info.app_category_agg")
    fun findAllAppCategoryAggData(): List<AppCategoryAggData>
}

我收到此错误

Query; CQL [SELECT * FROM analytics_info.app_category_agg]; Codec not found for requested operation: [map<varchar, int> <-> java.util.Map]; nested exception is com.datastax.driver.core.exceptions.CodecNotFoundException: Codec not found for requested operation: [map<varchar, int> <-> java.util.Map]

我该如何解决?我读过有关制作编解码器的信息,但对我来说不是很清楚

【问题讨论】:

  • 是 Spring Data cassandra,还是来自 Java 驱动程序的对象映射器?你用的是什么驱动版本?你能展示一下你是如何使用它的吗?
  • 是的,它的春季数据。我还添加了模型和存储库文件
  • 我不是 100% 确定,但是 Spring Data Cassandra 存在嵌套集合的问题...
  • 能否提出解决方案?最好在 Cassandra 中使用用户定义的类型或定义 Typecodec
  • 让我试试 Kotlin 和驱动 3.x...对象映射器也支持 Kotlin:docs.datastax.com/en/developer/java-driver/4.7/manual/mapper/…(虽然我没有尝试过)

标签: java kotlin cassandra datastax datastax-java-driver


【解决方案1】:

我用你的结构创建了一个表,并用示例数据填充它:

insert into app_category_agg (category, app_count, sp_count, subscriber_count, window_revenue, top_apps) 
values('test', 2, 1, 10, 100, [{'t1':1, 't2':2}]);

对于来自 Java 驱动程序 3 的对象映射器,工作代码如下。

类声明:

import com.datastax.driver.mapping.MappingManager
import com.datastax.driver.mapping.annotations.Column
import com.datastax.driver.mapping.annotations.PartitionKey
import com.datastax.driver.mapping.annotations.Table

@Table(keyspace = "test", name = "app_category_agg")
class AppCategoryAggData {

    @PartitionKey
    lateinit var category: String

    @Column(name = "app_count")
    var appCount: Int = 0

    @Column(name = "sp_count")
    var spCount: Int = 0

    @Column(name = "subscriber_count")
    var subscriberCount: Int = 0

    @Column(name = "window_revenue")
    var windowRevenue: Long = 0

    @Column(name = "top_apps")
    var topApps: List<Map<String, Int>> = emptyList()

    override fun toString(): String {
        return "AppCategoryAggData(category='$category', appCount=$appCount, spCount=$spCount, subscriberCount=$subscriberCount, windowRevenue=$windowRevenue, topApps=$topApps)"
    }
}

主函数 - 它首先从 Kotlin 代码中插入数据,然后读取我预先插入的数据:

import com.datastax.driver.core.Cluster

object KtTestObjMapper {
    @JvmStatic
    fun main(args: Array<String>) {
        val cluster = Cluster.builder()
                .addContactPoint("10.101.34.176")
                .build()
        val session = cluster.connect()

        val manager = MappingManager(session)
        val mapper = manager.mapper(AppCategoryAggData::class.java)

        val appObj = AppCategoryAggData()
        appObj.category = "kotlin"
        appObj.appCount = 5
        appObj.spCount = 10
        appObj.subscriberCount = 50
        appObj.windowRevenue = 10000
        appObj.topApps = listOf(mapOf("t2" to 2))
        mapper.save(appObj)

        val obj2 = mapper.get("test")
        print("obj2=$obj2")

        session.close()
        cluster.close()
    }
}

当我运行此代码时,我收到以下输出:

Object from =AppCategoryAggData(category='test', appCount=2, spCount=1, subscriberCount=10, windowRevenue=100, topApps=[{t1=1, t2=2}])

当我使用 cqlsh 从表中选择数据时,我看到 Kotlin 插入了数据:

cqlsh:test> SELECT * from app_category_agg ;

 category | app_count | sp_count | subscriber_count | top_apps             | window_revenue
----------+-----------+----------+------------------+----------------------+----------------
     test |         2 |        1 |               10 | [{'t1': 1, 't2': 2}] |            100
   kotlin |         5 |       10 |               50 |          [{'t2': 2}] |          10000

(2 rows)

full code is in my repository。该解决方案的一个缺点是它基于 Java 驱动程序 3.x,这是该驱动程序的先前主要版本。如果您对它没有严格的要求,建议使用最新的主要版本 - 4.x,它同时适用于 Cassandra 和 DSE,并且有很多新功能。

虽然新版本中的对象映射器工作方式不同——而不是运行时注解,而是使用编译注解,所以代码看起来不同,我们需要configure compilation process differentlyit could be more complicated compared to driver 3.x,但代码本身可以更简单(full code is here)。

我们需要定义数据类(entity):

@Entity
@CqlName("app_category_agg")
data class AppCategoryAggData(
    @PartitionKey var category: String,
    @CqlName("app_count") var appCount: Int? = null,
    @CqlName("sp_count") var spCount: Int? = null,
    @CqlName("subscriber_count") var subscriberCount: Int? = null,
    @CqlName("window_revenue") var windowRevenue: Long? = null,
    @CqlName("top_apps") var topApps: List<Map<String, Int>>? = null
) {
    constructor() : this("")
}

Define the DAO interface 有 2 个操作(insertfindByCategory):

@Dao
interface AppCategoryAggDao {
    @Insert
    fun insert(appCatAgg: AppCategoryAggData)

    @Select
    fun findByCategory(appCat: String): AppCategoryAggData?
}

Define the Mapper获取DAO:

@Mapper
interface AppCategoryMapper {
    @DaoFactory
    fun appCategoryDao(@DaoKeyspace keyspace: CqlIdentifier?): AppCategoryAggDao?
}

并使用它:

object KtTestObjMapper {
    @JvmStatic
    fun main(args: Array<String>) {
        val session = CqlSession.builder()
                .addContactPoint(InetSocketAddress("10.101.34.176", 9042))
                .build()

        // get mapper - please note that we need to use AppCategoryMapperBuilder
        // that is generated by annotation processor
        val mapper: AppCategoryMapper = AppCategoryMapperBuilder(session).build()

        val dao: AppCategoryAggDao? = mapper.appCategoryDao(CqlIdentifier.fromCql("test"))

        val appObj = AppCategoryAggData("kotlin2",
                10, 11, 12, 34,
                listOf(mapOf("t2" to 2)))
        dao?.insert(appObj)

        val obj2 = dao?.findByCategory("test")
        println("Object from =$obj2")

        session.close()
    }
}

相比Java的变化是我们需要使用生成的类AppCategoryMapperBuilder来获取AppCategoryMapper的实例:

val mapper: AppCategoryMapper = AppCategoryMapperBuilder(session).build()

【讨论】:

  • 感谢您的回答,我在模式中使用用户定义类型作为快速修复,因此我可以重用现有代码。我也会试试这个。
  • 我为 Java 驱动程序 4.x 的对象映射器添加了一个代码示例
猜你喜欢
  • 2019-01-03
  • 2019-05-27
  • 2018-06-08
  • 2018-10-16
  • 2017-09-25
  • 1970-01-01
  • 1970-01-01
  • 2017-03-24
  • 2017-02-16
相关资源
最近更新 更多