Кодек для запрошенной операции не найден: [map‹varchar, int› ‹-› java.util.Map]; выпуск Apache Cassandra

У меня есть таблица с полями

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]

как я могу это решить? Я читал о создании кодеков, но мне это не очень понятно


person wink_lol_coder    schedule 02.07.2020    source источник
comment
это Spring Data Cassandra или средство сопоставления объектов из драйвера Java? Какую версию драйвера вы используете? Можете ли вы показать, как вы его используете?   -  person Alex Ott    schedule 02.07.2020
comment
да, его весенние данные. Я также добавил файлы модели и репозитория   -  person wink_lol_coder    schedule 02.07.2020
comment
Я не уверен на 100%, но у Spring Data Cassandra есть проблема с вложенными коллекциями...   -  person Alex Ott    schedule 02.07.2020
comment
можно ли предложить решение? Лучше использовать пользовательский тип в Cassandra или определить Typecodec   -  person wink_lol_coder    schedule 02.07.2020
comment
Позвольте мне попробовать Kotlin с драйвером 3.x... Если у вас нет строгих требований оставаться на 3.x, я бы рекомендовал начать использовать новый драйвер - 4.x, и его объектный картограф также поддерживает Kotlin : docs.datastax.com/en/ developer/java-driver/4.7/manual/mapper/ (хотя я не пробовал)   -  person Alex Ott    schedule 02.07.2020
comment
Не могли бы вы предложить дизайнерское решение?   -  person wink_lol_coder    schedule 02.07.2020


Ответы (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)

полный код находится в моем репозитории. Единственным недостатком этого решения является то, что оно основано на драйвере Java 3.x, который является предыдущим основным выпуском драйвера. Если у вас нет строгих требований к этому, рекомендуется использовать последний основной выпуск - 4.x, который работает как с Cassandra, так и с DSE и имеет много новых функций.

Хотя картограф объектов в новой версии работает по-другому - вместо аннотаций времени выполнения используются аннотации компиляции, поэтому код выглядит по-другому, и нам нужно настроить процесс компиляции по-другому и может быть сложнее по сравнению с драйвером 3.x, но сам код может быть проще (полный код здесь).

Нам нужно определить класс данных (сущность):

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

Определите интерфейс DAO с помощью двух операций. (insert и findByCategory):

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

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

Определите 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()
person Alex Ott    schedule 02.07.2020
comment
спасибо за ответ, чувак, я выбрал пользовательский тип в схеме в качестве быстрого исправления, чтобы я мог повторно использовать существующий код. Я тоже попробую. - person wink_lol_coder; 02.07.2020
comment
Я добавил пример кода для сопоставления объектов драйвера Java 4.x. - person Alex Ott; 02.07.2020