Spring Data JDBC - один ко многим - Котлин

Я использую весеннюю загрузку 2.1.1.RELEASE и spring-data-jdbc 1.0.3.RELEASE, Kotlin 1.3.10.

У меня есть следующие простые определения классов в kotlin:

@Table(value = "CUSTOMER")
data class Customer(
    @Id
    var id: Long?,
    @Column("NAME")
    var name: String,
    @Column("PUBLICATION_NAME")
    var publicationName: String,
    @Column("START_DATE")
    var startDate: String,
    @Column("END_DATE")
    var endDate: String,

    var items: MutableSet<Item>

)

@Table(value = "ITEM")
data class Item  (
    @Id
    var id: Long?,
    @Column("name")
    var name: String,
    @Column("title")
    var title: String,
    @Column("weight")
    var weight: Double
)

И следующие определения таблицы базы данных:

CREATE TABLE customer(
  id                SERIAL PRIMARY KEY,
  name              VARCHAR(256) NOT NULL,
  publication_name  VARCHAR(128) NOT NULL,
  start_date        VARCHAR(10),
  end_date          VARCHAR(10)
);

CREATE TABLE item(
  id               SERIAL PRIMARY KEY,
  customer         INTEGER,
  name             VARCHAR(256)     NOT NULL,
  title            VARCHAR(128),
  weight           DOUBLE PRECISION NOT NULL
);

Я получаю следующую ошибку:

org.springframework.data.mapping.MappingException: Could not read property @org.springframework.data.annotation.Id()private java.lang.Long net.service.model.query.Item.id from result set!

    at org.springframework.data.jdbc.core.EntityRowMapper.readFrom(EntityRowMapper.java:130)
    at org.springframework.data.jdbc.core.EntityRowMapper.readEntityFrom(EntityRowMapper.java:143)
    at org.springframework.data.jdbc.core.EntityRowMapper.readFrom(EntityRowMapper.java:124)
    at org.springframework.data.jdbc.core.EntityRowMapper.lambda$createInstance$0(EntityRowMapper.java:167)
    at org.springframework.data.relational.core.conversion.BasicRelationalConverter$ConvertingParameterValueProvider.getParameterValue(BasicRelationalConverter.java:251)
    at org.springframework.data.convert.KotlinClassGeneratingEntityInstantiator$DefaultingKotlinClassInstantiatorAdapter.extractInvocationArguments(KotlinClassGeneratingEntityInstantiator.java:230)
    at org.springframework.data.convert.KotlinClassGeneratingEntityInstantiator$DefaultingKotlinClassInstantiatorAdapter.createInstance(KotlinClassGeneratingEntityInstantiator.java:204)
    at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:84)
    at org.springframework.data.relational.core.conversion.BasicRelationalConverter.createInstance(BasicRelationalConverter.java:141)
    at org.springframework.data.jdbc.core.EntityRowMapper.createInstance(EntityRowMapper.java:160)
    at org.springframework.data.jdbc.core.EntityRowMapper.mapRow(EntityRowMapper.java:71)
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:94)
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:61)
    at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:679)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:669)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:694)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:748)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:235)
    at org.springframework.data.jdbc.core.DefaultDataAccessStrategy.findById(DefaultDataAccessStrategy.java:204)
    at org.springframework.data.jdbc.core.JdbcAggregateTemplate.findById(JdbcAggregateTemplate.java:135)
    at org.springframework.data.jdbc.repository.support.SimpleJdbcRepository.findById(SimpleJdbcRepository.java:66)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:99)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy95.findById(Unknown Source)
    at net.service.model.query.repository.CustomerRepositoryV2Test.should save Customer with all Items(CustomerRepositoryV2Test.kt:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:515)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:105)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
    at java.util.ArrayList.forEach(ArrayList.java:1249)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
    at java.util.ArrayList.forEach(ArrayList.java:1249)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.h2.jdbc.JdbcSQLException: Column "items_id" not found [42122-197]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:357)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.jdbc.JdbcResultSet.getColumnIndex(JdbcResultSet.java:3148)
    at org.h2.jdbc.JdbcResultSet.get(JdbcResultSet.java:3247)
    at org.h2.jdbc.JdbcResultSet.getObject(JdbcResultSet.java:529)
    at org.springframework.data.jdbc.core.EntityRowMapper.readFrom(EntityRowMapper.java:127)
    ... 88 more

Из весенней документации:"Ожидается, что таблица ссылочной сущности будет иметь дополнительный столбец с именем, совпадающим с таблицей ссылочной сущности. Вы можете изменить это имя, реализовав NamingStrategy.getReverseColumnName(свойство RelationalPersistentProperty)".

Что я делаю неправильно?

Обновление: добавление коллекции наборов Java также не помогло:

var items: java.util.HashSet<Item> = java.util.HashSet()

Обновление 2: все еще не исправлено, добавлен воспроизводимый пример проекта на github:

https://github.com/ielkhalloufi/spring-data-jdbc-example

3 ответа

Решение

Попробуйте переместить набор из конструктора в тело, как

@Table(value = "CUSTOMER")
data class Customer(
    @Id
    var id: Long?,
    @Column("NAME")
    var name: String,
    @Column("PUBLICATION_NAME")
    var publicationName: String,
    @Column("START_DATE")
    var startDate: String,
    @Column("END_DATE")
    var endDate: String
){
    var items: MutableSet<Item> = HashSet()
}

И отредактируйте свой тест как

val gasLineItem = Customer(
            id = null,
            name = "name",
            publicationName = "",
            startDate = "2018-12-01",
            endDate = "2019-02-01"

    ).apply {
        items = mutableSetOf(
                gasCreative1,
                gasCreative2
        )
    }

Похоже, что это ошибка, связанная со свойствами коллекции, которые устанавливаются с помощью конструктора.

Kotlin создает конструктор со всеми атрибутами, включая Set, К сожалению, это вызывает ошибку, из-за которой Spring Data JDBC пытается загрузить содержащуюся сущность из ResultSet внешней сущности. Вместо этого он должен выполнить другой выбор, который он делает, когда свойство устанавливается только через setter или "wither".

Пожалуйста, отправьте вопрос по адресу https://jira.spring.io/projects/DATAJDBC

В качестве обходного пути исключите коллекционные атрибуты из конструктора. Поскольку я почти ничего не знаю о Kotlin, я не могу сказать вам, как это сделать.

Вы также можете использовать @PersistenceConstructor сказать Spring Data JDBC явно конструктор, который не включает в себя Set (что является причиной ошибки). Например:

@Table(value = "CUSTOMER")
data class Customer(
    @Id
    var id: Long?,
    @Column("NAME")
    var name: String,
    @Column("PUBLICATION_NAME")
    var publicationName: String,
    @Column("START_DATE")
    var startDate: String,
    @Column("END_DATE")
    var endDate: String,
    var items: MutableSet<Item>
) {

    @PersistenceConstructor
    constructor(
        id: Long?, name: String, publicationName: String, startDate: String, endDate: String
    ) : this(
        id,
        name,
        publicationName,
        startDate,
        endDate,
        hashSetOf()
    )
}
Другие вопросы по тегам