Spring Data JDBC и отношение Многие-ко-многим

Читаю Spring in Action (6 издание). Код из раздела с Spring Data JDBC упрямо отказывается работать. Ниже я максимально упростил, написанное в книге (3 простейшие таблицы в базе и 2 класса в коде), при этом 1 в 1 воспроизвел ошибку.

База данных H2.

Создал 3 таблицы (код из schema.sql): Test_First и Test_Second связаны между собой таблицей Refs. Принцип построения таблиц, как в книге.

create table if not exists Test_First (
    id identity,
    name_first varchar(50) not null
);

create table if not exists Test_Second (
    id identity,
    name_second varchar(50) not null
);

create table if not exists Refs (
    test_first bigint not null,
    test_first_key bigint not null,
    test_second bigint not null
);

alter table Refs add foreign key (test_second) references Test_Second(id);

Сразу заполнил таблицу Test_Second (код из data.sql):

delete from Test_First;
delete from Test_Second;
delete from Refs;

insert into Test_Second (name_second) values ('1_S_Name');
insert into Test_Second (name_second) values ('2_S_Name');
insert into Test_Second (name_second) values ('3_S_Name');
insert into Test_Second (name_second) values ('4_S_Name');

Теперь код программы. Язык - Kotlin.

Таблицы. TestFirst кроме прочего содержит массив из TestSecond.

@Table
class TestFirst(
    @Column("NAME_FIRST") val name: String,
) {
    @Id private var id: Long? = null
    private val seconds: MutableList<TestSecond> = mutableListOf()

    fun addSecond(second: TestSecond) {
        seconds.add(second)
    }
}

и

@Table
class TestSecond(
    @Column("NAME_SECOND") val name: String,
) {
    @Id private var id: Long? = null
}

Репозитории для обеих таблиц наследуются от CrudRepository без каких-либо дополнительных изменений.

Собственно вот код, который сначала получает все данные из таблицы Test_Second, добавляет некоторые из них в экземпляр класса TestFirst и пытается его сохранить.

@Autowired
lateinit var firstRepo: TestFirstRepo
@Autowired
lateinit var secondRepo: TestSecondRepo

@Bean
fun command(): CommandLineRunner = CommandLineRunner {
    val secondList: List<TestSecond> = secondRepo.findAll().toList()
    val first1 = TestFirst("1_F_NAME").apply {
        addSecond(secondList[0])
        addSecond(secondList[1])
        addSecond(secondList[2])
    }
    firstRepo.save(first1)
    val first2 = TestFirst("2_F_NAME").apply {
        addSecond(secondList[1])
        addSecond(secondList[2])
        addSecond(secondList[3])
    }
    firstRepo.save(first2)
}

В общем, где-то тут и возникает ошибка. Будто JDBC не понимает, что ему нужно создать запись в таблице Test_First, а после этого добавить запись в таблицу Refs. А я не знаю, как ему это объяснить.

Вот сама ошибка:

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Столбец "TEST_FIRST" не найден
Column "TEST_FIRST" not found; SQL statement:
INSERT INTO "TEST_SECOND" ("NAME_SECOND", "ID", "TEST_FIRST", "TEST_FIRST_KEY") VALUES (?, ?, ?, ?) [42122-214]

Книга написана для Java, но не думаю, что в этом большая проблема. И вроде как такой код должен работать, но нет. Помогите разобраться.


Ответы (1 шт):

Автор решения: he1ex-tG

Разобрался. Похоже, что в книге ошибка. Для разрешения вопроса нужно следующее.

  1. Создать в проекте класс, идентичный таблице Refs, а также немного изменить класс TestSecond:
@Table
class Refs(
    @Column("TEST_SECOND")
    val id: Long?,
)

@Table
class TestSecond(
    @Column("NAME_SECOND") val name: String,
) {
    @Id var id: Long? = null
}
  1. Внести изменения в класс TestFirst - теперь он будет накапливать экземпляры класса Refs, а не TestSecond (как раз об этом в книге ни слова):
@Table
class TestFirst(
    @Column("NAME_FIRST") val name: String,
) {
    @Id
    private var id: Long? = null
    @MappedCollection(idColumn = "TEST_FIRST")
    private val seconds: MutableList<Refs> = mutableListOf()

    fun addSecond(second: TestSecond) {
        seconds.add(Refs(second.id))
    }
}

Как видно, формально мы вызываем метод, в который передаем экземпляр TestSecond, вместо которого в массив добавляется новый экземпляр Refs.

В базе данных изменения не нужны.

→ Ссылка