Codec not found for requested operation: [map<varchar, int> <-> java.util.Map]; issue Apache Cassandra

浪尽此生 提交于 2020-07-09 05:30:23

问题


I have a table with the fields

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

when I try to map it to kotlin model

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

I get this error

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]

how can I resolve it? I read about making codecs but it's not very much clear to me


回答1:


I've created a table with your structure, and populated it with sample data:

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

For object mapper from the Java driver 3, the working code is following.

Class declaration:

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)"
    }
}

Main function - it first inserts data from Kotlin code, and then read the data that I pre-inserted:

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

When I run this code, I receive following output:

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

and when I select data from table using cqlsh, I see that data were inserted by 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)

The full code is in my repository. The one drawback of this solution is that it's based on the Java driver 3.x that is previous major release of the driver. If you don't have strict requirement for it, it's recommended to use latest major release - 4.x, that works with both Cassandra & DSE, and has a lot of new functionality.

Although the object mapper in new version works differently - instead of run-time annotations, the compile annotations are used, so code looks differently, and we need to configure compilation process differently, and it could be more complicated compared to driver 3.x, but code itself could be simpler (full code is here).

We need to define data class (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 with 2 operations (insert and findByCategory):

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

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

Define the Mapper to obtain the DAO:

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

And use it:

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

The change compared to Java is that we need to use the generated class AppCategoryMapperBuilder to obtain the instance of AppCategoryMapper in:

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


来源:https://stackoverflow.com/questions/62694080/codec-not-found-for-requested-operation-mapvarchar-int-java-util-map

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!