首页 > 解决方案 > Spring Boot 2.5.0 忽略了 @Column (Cassandra) 注释(适用于 2.4.6)

问题描述

在尝试使用spring-boot-starter-data-cassandrafrom Spring Boot 2.4.6to更新项目时2.5.0,我遇到了我的@Column注释被忽略的问题。

使用以下注释

    @Column("blabla")
    val buz: Long

导致此错误:

Query; CQL [INSERT INTO bar (baz,buz) VALUES (?,?)]; Undefined column name buz; nested exception is com.datastax.oss.driver.api.core.servererrors.InvalidQueryException: Undefined column name buz

因此查询使用buz而不是blabla按预期使用。使用 Spring Boot2.4.6而不是2.5.0它可以正常工作。有什么改变,所以我需要调整我的代码,还是这是一个错误?

问题似乎来自spring-data-cassandra,它使用 Spring Boot 更新。

看起来整个@Column注释都被忽略了,因为forceQuote = true我添加它时也没有使用它。

可以使用以下最小示例 ( docker build .) 来重现错误:

Dockerfile

FROM openjdk:11-jdk-slim
WORKDIR /home/test
ADD . /home/test
RUN ./gradlew test

build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

val kotlinVersion = "1.5.10"
val springBootVersion = "2.4.6"

plugins {
    val pluginKotlinVersion = "1.5.10"
    val pluginSpringBootVersion = "2.5.0"
    id("org.springframework.boot") version pluginSpringBootVersion
    kotlin("jvm") version pluginKotlinVersion
    kotlin("plugin.spring") version pluginKotlinVersion
    kotlin("plugin.jpa") version pluginKotlinVersion
}

group = "com.acme"
version = "1.0.0-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
    mavenCentral()
}

dependencies {

    implementation(
        group = "org.springframework.boot",
        name = "spring-boot-starter-data-cassandra",
        version = springBootVersion
    )

    // 3.1.9 -> fine
    // 3.2.0 -> broken
    // 3.2.1 -> broken
    implementation(group = "org.springframework.data", name = "spring-data-cassandra", version = "3.2.0")

    implementation(group = "org.springframework.boot", name = "spring-boot-starter-web", version = springBootVersion)

    implementation(group = "org.jetbrains.kotlin", name = "kotlin-reflect", version = kotlinVersion)

    implementation(
        group = "org.cassandraunit",
        name = "cassandra-unit-spring",
        version = "4.3.1.0"
    ) {
        exclude(group = "org.hibernate")
        testImplementation(group = "com.google.guava", name = "guava") {
            version {
                // https://github.com/jsevellec/cassandra-unit/issues/248
                // https://issues.apache.org/jira/browse/CASSANDRA-15245
                strictly("18.0")
            }
        }
    }

    testImplementation(
        group = "org.springframework.boot",
        name = "spring-boot-starter-test",
        version = springBootVersion
    )

}

tasks {
    withType<KotlinCompile> {
        kotlinOptions {
            freeCompilerArgs = listOf("-Xjsr305=strict")
            jvmTarget = "11"
        }
    }

    withType<Test> {
        testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
    }
}

settings.gradle.kts

rootProject.name = "acmetest"

src/main/kotlin/com/acme/Application.kt

package com.acme

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.cassandra.core.mapping.Column
import org.springframework.data.cassandra.core.mapping.PrimaryKey
import org.springframework.data.cassandra.core.mapping.Table
import org.springframework.data.cassandra.repository.CassandraRepository
import org.springframework.stereotype.Repository

@SpringBootApplication
class Application

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

@Table("bar")
class Bar(

    @PrimaryKey("baz")
    val baz: Long,

    @Column("blabla")
    val buz: Long
)

@Repository
interface BarRepository : CassandraRepository<Bar, Long>

src/main/resources/application.yml

spring:
  data:
    cassandra:
      contact-points: localhost
      port: 9142
      keyspace_name: foo
      local-datacenter: datacenter1

src/test/kotlin/com/acme/integration/TestFullStack.kt

package com.acme.integration


import com.acme.Bar
import com.acme.BarRepository
import org.cassandraunit.spring.CassandraDataSet
import org.cassandraunit.spring.CassandraUnitDependencyInjectionTestExecutionListener
import org.cassandraunit.spring.EmbeddedCassandra
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.ActiveProfiles
import org.springframework.test.context.TestExecutionListeners
import org.springframework.test.context.junit4.SpringRunner

@ActiveProfiles("test")
@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestExecutionListeners(
    listeners = [CassandraUnitDependencyInjectionTestExecutionListener::class],
    mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
@CassandraDataSet(value = ["cql/foo.cql"], keyspace = "foo")
@EmbeddedCassandra
class TestFullStack {

    @Autowired
    lateinit var barRepository: BarRepository

    @Test
    fun `test init`() {
        barRepository.save(Bar(1, 2))
    }
}

src/test/resources/cql/foo.cql

DROP KEYSPACE IF EXISTS foo;

CREATE KEYSPACE foo WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1};

CREATE TABLE foo.bar
(
    baz     bigint,
    blabla  bigint,
    PRIMARY KEY (baz)
);

标签: spring-bootkotlincassandra

解决方案


好的,问题似乎在于Bar已经在构造函数中声明的成员。即,替换这个

@Table("bar")
class Bar(

    @PrimaryKey("baz")
    val baz: Long,

    @Column("blabla")
    val buz: Long
)

接着就,随即

@Table("bar")
class Bar(baz: Long, buz: Long) {

    @PrimaryKey("baz")
    val baz: Long = baz

    @Column("blabla")
    val buz: Long = buz
}

让它再次工作。

两者的版本3.1.9org.springframework.data:spring-data-cassandra可以正常工作,但第一个版本在更新到3.2.0. 我们打开了一个问题:https ://github.com/spring-projects/spring-data-cassandra/issues/1136


编辑 2022-02-01:以下也适用:

@Table("bar")
class Bar(

    @PrimaryKey("baz")
    val baz: Long,

    @field:Column("blabla")
    val buz: Long
)

推荐阅读