未找到请求操作的编解码器:[map<varchar, int> <-> java.util.Map];发布 Apache Cassandra

Posted

技术标签:

【中文标题】未找到请求操作的编解码器:[map<varchar, int> <-> java.util.Map];发布 Apache Cassandra【英文标题】:Codec not found for requested operation: [map<varchar, int> <-> java.util.Map]; issue 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/…(虽然我没有尝试过) 【参考方案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 differently和it 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 的对象映射器添加了一个代码示例

以上是关于未找到请求操作的编解码器:[map<varchar, int> <-> java.util.Map];发布 Apache Cassandra的主要内容,如果未能解决你的问题,请参考以下文章

关于Tomcat上请求的编解码问题

文件的编解码操作

C对ASN.1的编解码环境搭建

像cssdeck这样的编解码功能[关闭]

如何在 Xcode 中使用 OpenCV 3.4.14 识别 .mp4 视频文件的编解码器?

Android开发老生新谈:从OkHttp原理看网络请求