如何使用Spring Boot和Cassandra将枚举作为普通项保留? [英] How to persist enums as ordinals with Spring Boot and Cassandra?

查看:179
本文介绍了如何使用Spring Boot和Cassandra将枚举作为普通项保留?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

To the enum field of my entity I have added @CassandraType(type = DataType.Name.INT). However not the ordinal of the enum, but the string representation instead, is used in the statement sent to Cassandra. Thus I get the following error:

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)

下面您可以找到一个最小的示例,重现该问题.

Below you can find a minimal example, reproducing the problem.

我在做什么错了?

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>

test/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_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)
);

test/build.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

由于它似乎是一个错误,所以我打开了一个Jira问题.

Since it seems to be a bug, I just opened a Jira issue.

推荐答案

我已经尝试了一段时间了,看来我终于明白了!

I've been trying to get this working for quite awhile and it seems I finally got it!

我遇到了与编解码器相同的问题...我不知道为什么这不起作用.根据他们的文档,您所做的完全正确.

I was running into the same issue you were with the codec...I have no idea why that's not working. According to their documentation you were doing it exactly right.

因此,我实现了自己的Cassandra写转换器.见下文

So I implemented my own Cassandra Write Converter. See below

@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.这为您提供了针对不同类型的枚举使用多个这些枚举的可能性,出于某种原因,您可能想从中写入其他自定义值,而不是始终转换所有枚举.

This should on every write you do to Cassandra convert all enums it sees of type MyEnum to an Int using the overridden converter. This opens you up to the possibility of having multiple of these for different types of Enums where maybe for some reason you would like to write other custom values from them instead of always converting all enums.

希望这行得通!

EDIT 请注意在删除每个转换器上的{}实例并向CassandraCustomConversions注册ReadingConverter

EDIT Note the change in removing { } for INSTANCE on each converter, and registering the ReadingConverter with the CassandraCustomConversions

这篇关于如何使用Spring Boot和Cassandra将枚举作为普通项保留?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆