如何使用SpringBoot和cassandra将枚举持久化为序数?

cnjp1d6j  于 2021-06-10  发布在  Cassandra
关注(0)|答案(2)|浏览(315)

添加到实体的枚举字段 @CassandraType(type = DataType.Name.INT) . 但是,在发送给cassandra的语句中使用的不是枚举的序号,而是字符串表示。因此,我得到以下错误:

org.springframework.data.cassandra.CassandraInvalidQueryException: SessionCallback; CQL [INSERT INTO thing (thing_id,some_enum) VALUES (1,'Foo');]; Expected 4 or 0 byte int (3); nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Expected 4 or 0 byte int (3)

下面你可以找到一个最小的例子,再现问题。
我做错什么了?

测试/src/main/kotlin/enumtest/application.kt

package enumtest

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

test/src/main/kotlin/enumtest/someenum.kt

package enumtest

enum class SomeEnum {
    Foo,
    Bar
}

test/src/main/kotlin/enumtest/thing.kt

package enumtest

import com.datastax.driver.core.DataType
import org.springframework.data.cassandra.core.cql.PrimaryKeyType
import org.springframework.data.cassandra.core.mapping.CassandraType
import org.springframework.data.cassandra.core.mapping.Column
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn
import org.springframework.data.cassandra.core.mapping.Table

@Table("thing")
@Suppress("unused")
class Thing(

    @PrimaryKeyColumn(name = "thing_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
    var thingId: Long,

    @CassandraType(type = DataType.Name.INT)
    @Column("some_enum")
    var someEnum: SomeEnum

)

test/src/main/kotlin/enumtest/thingrepository.kt

package enumtest

import org.springframework.data.cassandra.repository.CassandraRepository
import org.springframework.stereotype.Repository

@Repository
interface ThingRepository : CassandraRepository<Thing, Long>

测试/src/main/resources/application.yml

spring:
  data:
    cassandra:
      contact-points: localhost
      port: 9142
      keyspace_name: enumtest

test/src/test/kotlin/enumtest/persistencetest.kt测试

package enumtest

import org.cassandraunit.spring.CassandraDataSet
import org.cassandraunit.spring.CassandraUnitDependencyInjectionTestExecutionListener
import org.cassandraunit.spring.EmbeddedCassandra
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.TestExecutionListeners
import org.springframework.test.context.junit4.SpringRunner

@RunWith(SpringRunner::class)
@SpringBootTest
@TestExecutionListeners(
    listeners = [CassandraUnitDependencyInjectionTestExecutionListener::class],
    mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
@CassandraDataSet(value = ["cql/cassandra_schema.cql"], keyspace = "enumtest")
@EmbeddedCassandra
class PersistenceTest {
    @Autowired
    lateinit var thingRepository: ThingRepository

    @Test
    fun `test save`() {
        thingRepository.save(Thing(1, SomeEnum.Foo))
        val things = thingRepository.findAll()
        Assert.assertEquals(1, things.size)
        val thing = things[0]
        Assert.assertEquals(SomeEnum.Foo, thing.someEnum)
    }
}

test/src/test/resources/cql/cassandra\u schema.cql测试

CREATE KEYSPACE IF NOT exists enumtest
WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1};

CREATE TABLE IF NOT exists enumtest.thing (
    thing_id     bigint,
    some_enum    int,
    PRIMARY KEY (thing_id)
);

测试/生成.gradle

plugins {
    id 'org.springframework.boot' version '2.1.4.RELEASE'
    id 'org.jetbrains.kotlin.jvm' version '1.3.30'
    id 'org.jetbrains.kotlin.plugin.spring' version '1.3.30'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
    maven { url "https://repository.apache.org/snapshots/" }
}

dependencies {
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter'
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-cassandra'
    implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8'
    implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect'

    testImplementation group: 'org.cassandraunit', name: 'cassandra-unit-spring', version: '3.5.0.1'
    testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
}

compileKotlin {
    kotlinOptions {
        freeCompilerArgs = ['-Xjsr305=strict']
        jvmTarget = '1.8'
    }
}

compileTestKotlin {
    kotlinOptions {
        freeCompilerArgs = ['-Xjsr305=strict']
        jvmTarget = '1.8'
    }
}

以下是最小示例的完整版本,可下载以方便实验:https://drive.google.com/open?id=1zzidhbwycaj4wxrze2samw8xrpaca8js
编辑:因为它似乎是一个bug,我刚刚打开了一个jira问题。

wgxvkvu9

wgxvkvu91#

我已经试着让这个工作了很长一段时间,似乎我终于得到了!
我遇到了和你在编解码器上遇到的相同的问题…我不知道为什么那不起作用。根据他们的文件你做得很对。
所以我实现了自己的cassandra写转换器。见下文

@Configuration
class CassandraConfig(val cluster: Cluster){

    @Bean
    fun setCustomCassandraConversions() = CassandraCustomConversions(listOf(EnumWriteConverter.INSTANCE, EnumReadConverter.INSTANCE))

    @WritingConverter
    enum class EnumWriteConverter : Converter<Enum<MyEnum>, Int> {
        INSTANCE;
        override fun convert(source: Enum<MyEnum>) = source.ordinal
    }
     @ReadingConverter
    enum class EnumReadConverter : Converter<Int, Enum<MyEnum>> {
        INSTANCE;
        override fun convert(source: Int) = MyEnum.values()[source]
    }
}

这应该在每次编写cassandra时使用重写的转换器将它看到的myenum类型的所有枚举转换为int。这使您可以为不同类型的枚举使用多个这样的值,因为某些原因,您可能希望从这些值中写入其他自定义值,而不是始终转换所有枚举。
希望这能奏效!
请注意删除{}的更改,例如在每个转换器上,并向cassandracustomconversions注册readingconverter

bzzcjhmw

bzzcjhmw2#

这是固定的,因为 Spring 启动版本 2.1.5 .
然而 @CassandraType 需要显式地放置在kotlin中的getter中,否则在运行时就看不到它。
实际上,这仅仅意味着替换:

@CassandraType(type = DataType.Name.INT)
    var someEnum: SomeEnum

因此:

@get: CassandraType(type = DataType.Name.INT)
    var someEnum: SomeEnum

相关问题