问题
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