有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在存在`@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 ...
}

我们只想提取idnamecode属性。让我们编写一个简单的投影界面:

@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进行投影

(动机:我们最终想要构造一个包含不相关表的属性的投影,所以我们使用@QueryLEFT 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()返回targetFooname,而getfooName()返回targetFoocode

因此,如果我们切换选择参数以再次匹配接口顺序

@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) 个答案

  1. # 1 楼答案

    最新版本的Hibernate(5.2.1以后)修复了使用投影时提到的排序问题。 对于较旧的hibernate版本,保持本机查询中的列/别名按字母顺序排列,这样就可以正常工作

    有关详细信息,您可能需要检查/排除以下方法

    org.springframework.data.repository.query.ResultProcessor.ProjectingConverter.toMap(Collection<?>, List<String>)