在存在`@Query`注释的情况下,通过接口方法顺序而不是名称来解析java Spring JPA投影?
我的理解是,在SpringJPA中创建投影时,字段将按名称进行解析
在使用@Query
注释时,情况似乎并非如此
这是预期的行为还是我的查询有问题
假设我有一个实体
@Entity
@Table(name = "foo")
public class Foo {
@Id
@GeneratedValue(strategy = IDENTITY)
private Integer id;
@Column(name = "code")
private String code;
@Column(name = "name")
private String name;
... much more that we are not interested in ...
}
我们只想提取id
、name
和code
属性。让我们编写一个简单的投影界面:
@Repository
public interface FooRepository extends JpaRepository<Foo, Integer> {
FooTestProjection findOneByCode(String code);
interface FooTestProjection {
Integer getId();
String getName();
String getCode();
}
}
以及验证我们正在做什么的测试:
@Test
void fooTestProjections() {
Foo targetFoo = repository.saveAndFlush(testData.getFoo());
getEntityManager().clear();
FooTestProjection testProjection = repository.findOneByCode(targetFoo.getCode());
assertThat(testProjection.getId()).isEqualTo(targetFoo.getId());
assertThat(testProjection.getName()).isEqualTo(targetFoo.getName());
assertThat(testProjection.getCode()).isEqualTo(targetFoo.getCode());
}
有效
这里的期望是按名称解析属性。因此,让我们在投影界面中切换getCode()
和getName()
:
interface FooTestProjection {
Integer getId();
String getCode();
String getName();
}
是的,仍然有效
现在,假设我们想要对所有的Foo
进行投影,并且我们想要使用@Query
进行投影
(动机:我们最终想要构造一个包含不相关表的属性的投影,所以我们使用@Query
到LEFT JOIN
来表示它们。)
因此,让我们调整我们的存储库方法:
@Repository
public interface FooRepository extends JpaRepository<Foo, Integer> {
@Query("SELECT" +
" f.id AS fooId," +
" f.name AS fooName," +
" f.code AS fooCode" +
" FROM Foo f"
)
List<FooTestProjection> findProjections();
interface FooTestProjection {
Integer getFooId();
String getFooName();
String getFooCode();
}
}
我们的测试:
@Test
void fooTestProjections() {
Foo targetFoo = repository.saveAndFlush(testData.getFoo());
getEntityManager().clear();
FooTestProjection testProjection = repository.findProjections().get(0);
assertThat(testProjection.getFooId()).isEqualTo(targetFoo.getId());
assertThat(testProjection.getFooName()).isEqualTo(targetFoo.getName());
assertThat(testProjection.getFooCode()).isEqualTo(targetFoo.getCode());
}
有效
测试绿色,一切正常,不是吗
编号
我们的期望是,Spring将查看我们构建的结果集,并通过我们定义的列别名将它们与投影接口匹配
事实并非如此
让我们在界面中切换getFooCode()
和getFooName()
,但不要在查询中切换:
@Query("SELECT" +
" f.id AS fooId," +
" f.name AS fooName," +
" f.code AS fooCode" +
" FROM Foo f"
)
List<FooTestProjection> findProjections();
interface FooTestProjection {
Integer getFooId();
String getFooCode();
String getFooName();
}
失败
为什么会失败?因为现在,所以getFooCode()
返回targetFoo
的name
,而getfooName()
返回targetFoo
的code
因此,如果我们切换选择参数以再次匹配接口顺序
@Query("SELECT" +
" f.id AS fooId," +
" f.code AS fooCode," +
" f.name AS fooName" +
" FROM Foo f"
)
List<FooTestProjection> findProjections();
interface FooTestProjection {
Integer getFooId();
String getFooCode();
String getFooName();
}
这个又起作用了
我发现这与直觉极为相悖,以至于我质疑我的质疑的有效性
如果有人能解释一下这种行为,我将不胜感激
# 1 楼答案
最新版本的Hibernate(5.2.1以后)修复了使用投影时提到的排序问题。 对于较旧的hibernate版本,保持本机查询中的列/别名按字母顺序排列,这样就可以正常工作
有关详细信息,您可能需要检查/排除以下方法