Ошибка обновления Spring Data R2dbc при обновлении до Spring Boot 3

Я попытался обновить один из моих примеров проектов до последней версии стека Spring Boot 3.0.5, исходные тесты Spring Data R2dbc не увенчались успехом.

Объект R2dbc выглядит следующим образом.

@Table(value = "posts")
data class Post(
    @Id
    @Column("id")
    val id: UUID? = null,

    @Column("title")
    var title: String,

    @Column("content")
    var content: String,

    @Column("status")
    var status: Status = Status.DRAFT,

    @Column("created_at")
    @CreatedDate
    val createdAt: LocalDateTime? = null,

    @Column("created_by")
    @CreatedBy
    val createdBy: String? = null,

    @Column("updated_at")
    @LastModifiedDate
    val updatedAt: LocalDateTime? = null,

    @Column("version")
    @Version
    @JsonIgnore
    val version: Long? = null,
)

PostSummary и PostRepository это:


data class PostSummary(var id: UUID, var title: String)

interface PostRepository :
        CoroutineCrudRepository<Post, UUID>, CoroutineSortingRepository<Post, UUID> {
    fun findByTitleContains(title: String): Flow<PostSummary>
    fun findByStatus(status: Status): Flow<Post>
}

Тесты репозитория такие.

@OptIn(ExperimentalCoroutinesApi::class)
@DataR2dbcTest
@Testcontainers
@Import(DataConfig::class)
class PostRepositoryTest {
    companion object {
        private val log = LoggerFactory.getLogger(PostRepositoryTest::class.java)

        @Container
        private val postgreSQLContainer: PostgreSQLContainer<*> = PostgreSQLContainer<Nothing>("postgres:12")

        @DynamicPropertySource
        @JvmStatic
        fun registerDynamicProperties(registry: DynamicPropertyRegistry) {
            registry.add("spring.r2dbc.url") {
                "r2dbc:postgresql://${postgreSQLContainer.host}:${postgreSQLContainer.firstMappedPort}/${postgreSQLContainer.databaseName}"
            }
            registry.add("spring.r2dbc.username") { postgreSQLContainer.username }
            registry.add("spring.r2dbc.password") { postgreSQLContainer.password }
        }
    }

    @Autowired
    lateinit var dbclient: DatabaseClient

    @Autowired
    lateinit var template: R2dbcEntityTemplate

    @Autowired
    lateinit var posts: PostRepository

    @BeforeEach
    fun setup() = runTest {
        val deleted = template.delete(Post::class.java).all().awaitSingle()
        log.debug("clean posts list before tests: $deleted")
    }

    @Test
    fun testDatabaseClientExisted() {
        assertNotNull(dbclient)
    }

    @Test
    fun testR2dbcEntityTemplateExisted() {
        assertNotNull(template)
    }

    @Test
    fun testPostRepositoryExisted() {
        assertNotNull(posts)
    }

    @Test
    fun testInsertAndQuery() = runTest {
        val data = Post(title = "test title", content = "test content")
        val saved = posts.save(data)
        // verify id is inserted.
        assertNotNull(saved.id)

        val existed = posts.findById(saved.id!!)!!
        log.debug("found existed post: $existed")
        //verify the saved data
        assertThat(existed.title).isEqualTo("test title")
        assertThat(existed.status).isEqualTo(Status.DRAFT)

        existed.apply {
            title = "update title"
            status = Status.PENDING_MODERATION
        }
        posts.save(existed)  // this line caused failure
        val updatedPosts = posts.findByTitleContains("update")

        //verify the updated title
        assertThat(updatedPosts.count()).isEqualTo(1)
        assertThat(updatedPosts.toList()[0].title).isEqualTo("update title")
    }

...
}

Я получил следующие исключения:

2023-04-15T12:18:33.376+08:00 DEBUG 8320 --- [in @coroutine#2] com.example.demo.PostRepositoryTest      : 
found existed post: Post(id=1ba70b74-8de6-4200-8953-40f180a08c8b, title=test title, content=test content, status=DRAFT, createdAt=2023-04-15T12:18:33.213805, createdBy=null, updatedAt=2023-04-15T12:18:33.213805, version=0)
2023-04-15T12:18:33.528+08:00 DEBUG 8320 --- [in @coroutine#2] o.s.r2dbc.core.DefaultDatabaseClient     : 
Executing SQL statement [UPDATE posts SET title = $1, content = $2, status = $3, created_at = $4, created_by = $5, updated_at = $6, version = $7 WHERE posts.id = $8 AND (posts.version = $9)]
2023-04-15T12:18:33.607+08:00 DEBUG 8320 --- [in @coroutine#2] o.s.r2dbc.core.DefaultDatabaseClient     : 
Executing SQL statement [SELECT posts.id, posts.title FROM posts WHERE posts.title LIKE $1]

org.springframework.data.mapping.model.MappingInstantiationException:
 Failed to instantiate com.example.demo.domain.model.Post 
using constructor fun <init>(java.util.UUID?, kotlin.String, kotlin.String, com.example.demo.domain.model.Status, java.time.LocalDateTime?, kotlin.String?, java.time.LocalDateTime?, kotlin.Long?): 
com.example.demo.domain.model.Post with arguments 
1ba70b74-8de6-4200-8953-40f180a08c8b,update title,null,null,null,null,null,null,248,null

    at org.springframework.data.mapping.model.
KotlinClassGeneratingEntityInstantiator$DefaultingKotlinClassInstantiatorAdapter.createInstance

Я не уверен, что он пытается создать объект при обновлении существующего объекта? В тесте я просто хочу обновить заголовок/статус, а не обновить содержимое, что вызвало это исключение.

Обновление: измените content на nullable, тест пройден. Но я считаю это неразумным.

Окончательный выбор вызвал проблему, которая не выбирает поле content. Хотя результатом запроса является Flow<PostSummary>, запрос по-прежнему пытается сначала создать экземпляр Post.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
1
0
112
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш анализ неверен. Оператор обновления проходит, и оператор позже SELECT posts.id, posts.title FROM posts не выбирает столбец content; Следовательно, вы не можете создать экземпляр объекта.

Да, ты прав. findByTitleContains возвращает Flow<PostSummary>, но PostSummary не включает поле content. Почему отображение результатов запроса здесь проблематично? Он сначала создал Post (но использовал поля в PostSummary), а затем преобразовал в PostSummary?

Hantsy 18.04.2023 09:21

Другие вопросы по теме