带有来自本机查询的参数的 Spring 实体动态计算字段

Posted

技术标签:

【中文标题】带有来自本机查询的参数的 Spring 实体动态计算字段【英文标题】:Spring entity dynamically calculated field with parameter from native query 【发布时间】:2018-10-12 08:23:59 【问题描述】:

我有一个有点复杂的实体,如下所示(注意具有更多字段的超类):

public class Question extends Entry 

    @OneToMany(orphanRemoval = true, mappedBy = "question")
    @JsonManagedReference
    private List<Answer> answers = new ArrayList<>();

    private Long viewCount = 0L;

    private Category category;

    @OneToMany(mappedBy = "question", fetch = FetchType.LAZY,
            cascade = CascadeType.ALL, orphanRemoval = true)
    private List<QuestionTranslation> translations = new ArrayList<>();

    @Transient
    private double distance;

distance 应在从本机查询检索结果集时从数据库计算。 例如

SELECT q.*, ST_Distance_Sphere(cast(q.location as geometry), ST_MakePoint(cast(?1 as double precision), cast(?2 as double precision))) as distance from question q

我不能使用@Formula 来注释我的字段distance,因为查询必须接受参数。

如何将 SQL 查询结果中的字段 distance 映射到我的实体字段 distance,同时让所有其他映射由 Hibernate 完成?

编辑

根据@gmotux 的建议,我创建了一个包装实体。

@Entity
@SqlResultSetMapping(
        name="MappingQ",
        entities=
                @EntityResult(
                        entityClass = QuestionWithDistance.class,
                        fields=
                                @FieldResult(name="distance",column="distance"),
                                @FieldResult(name="question",column="question")))
public class QuestionWithDistance
    @Id
    @GeneratedValue
    private String id;

    @OneToOne
    private Question question;
    private double distance;

查询

Query query = entityManager.createNativeQuery("SELECT q.*, 222.22 as distance from question q", "MappingQ");

但它总是失败

org.postgresql.util.PSQLException: The column name id1_15_0_ was not found in this ResultSet.

【问题讨论】:

你好@isADon,我有同样的问题(使用 mysql 代替)。你找到解决办法了吗? 【参考方案1】:

由于您需要额外的参数来计算字段,因此您确实不能使用@Formula,甚至不能使用getter 来计算字段。 不幸的是,对于您的情况,唯一想到的事情是,假设您正在为 Hibernate 使用基于 EntityManager 的配置,则利用其 @PostLoad 事件侦听器,您可以使用它来计算实体加载时的字段值,例如:

public class Question extends Entry 
    @PostLoad
    private void postLoad() 
        this.distance = DistanceCalculator.calculateDistance(Double param1,Double param2);       
        //other calculations
    

这当然只是一种解决方法,这意味着您必须在某处有一个静态方法来执行本机查询。 如果可能的话,我建议在您的要求中将“距离”概念从您的 Question 实体中分离出来,并在需要时使用本机 SQL 函数调用或服务方法进行计算。

【讨论】:

从 SQL 查询中检索结果集时,我已经使用 PostGIS 函数调用按距离对列表进行了排序。这意味着每个元素的距离已经从数据库中计算出来。我真的很想找到一种方法,让我的 Spring 应用程序不再进行计算,而是直接从数据库中获取值。 如果我理解正确,问题是您需要一个在持久性期间仅是瞬态的字段。在类似的情况下,我制作了一个信封并将我的查询自定义映射到该信封(即将您的距离字段与问题实体分开并将它们包装在具有2个字段的自定义实体类中:一个问题和一个非瞬态“距离”字段) .之后,您可以使用 SqlResultSetMapping 将您的查询映射到该包装器。然后,您就可以单独保存您的 Question 实体。 请看我的编辑。我是否正确理解了您的建议?

以上是关于带有来自本机查询的参数的 Spring 实体动态计算字段的主要内容,如果未能解决你的问题,请参考以下文章

请教关于Spring Data JPA动态查询参数的问题

Spring Data JPA 将原生查询结果映射到非实体 POJO

本机查询中未设置参数

Spring Data JPA中使用QueryDSL进行查询

为啥在本机查询 Hibernate 延迟加载的子实体中?

使用带有 LIKE 的 mysql 本机查询的 spring boot 搜索返回空