@Table Annotation is not honoured when joining on tables other than the aggregate root
Hi, I ran into a problem with a CrudRepository and aggregates that contain one-to-many relations and were annotated with @Table. Consider this example:
public interface AuthorRepository extends CrudRepository<AuthorRepository.AuthorRecord, String> {
@Table("author")
record AuthorRecord(@Id UUID id, String name, Set<Book> books) {}
record Book(String title) {}
}
with the following schema:
CREATE TABLE author
(
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
name text
);
CREATE TABLE book
(
author uuid,
title text
);
Now if I execute the following test (BaseTestConfiguration just initialises a connection to a Postgres database).
class AuthorRepositoryIntegrationTest extends BaseTestConfiguration {
@Autowired AuthorRepository authorRepository;
@Test
void should_persist_and_load_authors() {
// arrange
var authorRecord =
new AuthorRepository.AuthorRecord(
null, "authorName", Set.of(new AuthorRepository.Book("bookTitle")));
// act
authorRepository.save(authorRecord);
}
}
it throws this error:
Caused by: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO "book" ("author_record", "title") VALUES (?, ?)]; nested exception is org.postgresql.util.PSQLException: ERROR: column "author_record" of relation "book" does not exist
To me it looks like the @Table annotation is not honoured. Once I rename AuthorRecord to Author and drop the Annotation everything works fine. (Switching records to classes doesn't change the result.)
During debugging I stumbled upon org.springframework.data.relational.core.mapping.NamingStrategy#getTableName, were the table name is derived from the type. I assume the problem could be fixed by looking for the annotation before defaulting to type.getSimpleName(). I'm happy to try to submit a fix for it if necessary.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<testcontainers.version>1.16.2</testcontainers.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>${testcontainers.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
This behaviour is actually intended this way, and probably quite some users probably depend on it by now. Therefore the fix should probably be to document it accordingly.
We decided to split this issue into three.
The current one should be just the documentation update.
#1161 makes the behavior configurable. #1162 switches the default with the 3.0 release.
I'm happy to look into this (and #1161 and #1162) and will try to come up with a PR as soon as I find the time (hopefully this week).