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 шт):
Разобрался. Похоже, что в книге ошибка. Для разрешения вопроса нужно следующее.
- Создать в проекте класс, идентичный таблице
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
}
- Внести изменения в класс
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.
В базе данных изменения не нужны.