Skip to content

Commit b81c04a

Browse files
committed
Fix #{#entityName} SpEL expression in native queries to return table name
Fixes #3979 Native queries with SELECT * and #{#entityName} were failing because #{#entityName} was returning the entity name instead of the table name for native queries. This change: - Adds getTableName() method to JpaEntityMetadata interface with default implementation - Makes #{#entityName} return table name for native queries and entity name for JPQL queries - Removes instanceof checks for cleaner, type-safe code Added test to verify the fix works correctly.
1 parent d5d9332 commit b81c04a

File tree

5 files changed

+46
-2
lines changed

5 files changed

+46
-2
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.jpa.repository.query;
1717

1818
import jakarta.persistence.Entity;
19+
import jakarta.persistence.Table;
1920

2021
import org.springframework.core.annotation.AnnotatedElementUtils;
2122
import org.springframework.util.Assert;
@@ -53,4 +54,11 @@ public String getEntityName() {
5354
Entity entity = AnnotatedElementUtils.findMergedAnnotation(domainType, Entity.class);
5455
return null != entity && StringUtils.hasText(entity.name()) ? entity.name() : domainType.getSimpleName();
5556
}
57+
58+
@Override
59+
public String getTableName() {
60+
61+
Table table = AnnotatedElementUtils.findMergedAnnotation(domainType, Table.class);
62+
return null != table && StringUtils.hasText(table.name()) ? table.name() : getEntityName();
63+
}
5664
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,14 @@ public interface JpaEntityMetadata<T> extends EntityMetadata<T> {
3030
* @return
3131
*/
3232
String getEntityName();
33+
34+
/**
35+
* Returns the table name from the @Table annotation or defaults to the entity name.
36+
*
37+
* @return the table name
38+
* @since 4.0
39+
*/
40+
default String getTableName() {
41+
return getEntityName();
42+
}
3343
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/TemplatedQuery.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public static EntityQuery create(DeclaredQuery declaredQuery, JpaEntityMetadata<
8686

8787
ValueExpressionParser expressionParser = queryContext.getValueExpressionDelegate().getValueExpressionParser();
8888
String resolvedExpressionQuery = renderQueryIfExpressionOrReturnQuery(declaredQuery.getQueryString(),
89-
entityMetadata, expressionParser);
89+
entityMetadata, expressionParser, declaredQuery.isNative());
9090

9191
return EntityQuery.create(declaredQuery.rewrite(resolvedExpressionQuery), queryContext.getSelector());
9292
}
@@ -98,6 +98,17 @@ public static EntityQuery create(DeclaredQuery declaredQuery, JpaEntityMetadata<
9898
*/
9999
static String renderQueryIfExpressionOrReturnQuery(String query, JpaEntityMetadata<?> metadata,
100100
ValueExpressionParser parser) {
101+
return renderQueryIfExpressionOrReturnQuery(query, metadata, parser, false);
102+
}
103+
104+
/**
105+
* @param query, the query expression potentially containing a SpEL expression. Must not be {@literal null}.
106+
* @param metadata the {@link JpaEntityMetadata} for the given entity. Must not be {@literal null}.
107+
* @param parser Must not be {@literal null}.
108+
* @param isNative whether the query is a native SQL query.
109+
*/
110+
static String renderQueryIfExpressionOrReturnQuery(String query, JpaEntityMetadata<?> metadata,
111+
ValueExpressionParser parser, boolean isNative) {
101112

102113
Assert.notNull(query, "query must not be null");
103114
Assert.notNull(metadata, "metadata must not be null");
@@ -108,7 +119,10 @@ static String renderQueryIfExpressionOrReturnQuery(String query, JpaEntityMetada
108119
}
109120

110121
SimpleEvaluationContext evalContext = SimpleEvaluationContext.forReadOnlyDataBinding().build();
111-
evalContext.setVariable(ENTITY_NAME, metadata.getEntityName());
122+
123+
String entityNameValue = isNative ? metadata.getTableName() : metadata.getEntityName();
124+
125+
evalContext.setVariable(ENTITY_NAME, entityNameValue);
112126

113127
query = potentiallyQuoteExpressionsParameter(query);
114128

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,14 @@ void executesNativeQueryCorrectly() {
783783
assertThat(repository.findNativeByLastname("Matthews")).containsOnly(thirdUser);
784784
}
785785

786+
@Test // GH-3979
787+
void executesNativeQueryWithSelectStarCorrectly() {
788+
789+
flushTestUsers();
790+
791+
assertThat(repository.findNativeWithSelectStar("Matthews")).containsOnly(thirdUser);
792+
}
793+
786794
@Test // DATAJPA-132
787795
void executesFinderWithTrueKeywordCorrectly() {
788796

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,10 @@ List<String> findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter
676676
@Query(value = "VALUES (1)", nativeQuery = true)
677677
List<Integer> valuesStatementNative();
678678

679+
// GH-3979
680+
@Query(value = "select * from #{#entityName} where lastname = ?1", nativeQuery = true)
681+
List<User> findNativeWithSelectStar(String lastname);
682+
679683
// GH-2578
680684
@Query(value = "with sample_data as ( Select * from SD_User u where u.age > 30 ) \n select * from sample_data",
681685
nativeQuery = true)

0 commit comments

Comments
 (0)