如何在Hibernate

时间:2016-04-18 21:22:47

标签: java sql hibernate postgresql jpql

我有一个命名的原生查询:

@NamedNativeQueries({
    @NamedNativeQuery(
            name = "WeightLog.getAll",
            query = "select weightLogId, dateOfLog, weight, weight - lag(weight) over (ORDER BY dateOfLog) as change, userId from weightLog order by dateOfLog desc",
            resultClass = WeightLog.class)})

我在哪里"改变"使用滞后命令显示与此列中最后一个条目的差异。

在Entity中处理此问题的正确方法是什么,以便当我执行get时,我可以将其返回到UI但是当我执行创建/更新/删除时,它将被忽略?

我所做的就是这样注释:

@Column(insertable = false, updateable=false)
private Double change;

这意味着在我的getAll期间,它将显示在从我的REST端点返回的JSON中,并且在执行创建/更新时将被忽略。但是有了这个,我无法通过相应的REST端点从DB中删除,因为在我的DAO中使用delete方法我使用了get,它将返回"更改"。

删除DAO中的方法:

public WeightLog getById(final Long weightLogId){
    return get(weightLogId);
}

public Long deleteById(final Long weightLogId){
    final WeightLog weightLogToDelete = this.getById(weightLogId);
    this.delete(weightLogToDelete);
    return weightLogToDelete.getWeightLogId();
}

private void delete(final WeightLog weightLog){
    currentSession().delete(weightLog);
}

例外是:

ERROR [2016-04-18 21:06:32,365] io.dropwizard.jersey.errors.LoggingExceptionMapper:处理请求时出错:1f50d70af5fc5606 ! org.postgresql.util.PSQLException:错误:列weightlog0_.change不存在 !位置:51

@Transient并不好,看起来这两种方式都会被忽略,并且不会出现在任何CRUD操作中。

我认为我上面所做的完全是错误的做法,只是一个" hacky"到目前为止,我可以随意告诉我,我做了什么是完全错误的,并指出我正确的方向如何处理这种使用滞后的情况。如果可能的话,我更愿意使用注释。

为选项1编辑

遵循Dimitri的第一个选项,下面是如何返回到UI的json数据:

[
{
  "weightLogId": 15,
  "dateOfLog": "2016-04-30",
  "weight": 55,
  "userId": 1
},
-31.7]

单独更改,没有附加名称。这使得难以实际将这个属性打印到屏幕上吗?我在前端使用Backbone无法调用model.attribute或model.get(' attribte')。对于我的旧冷,格式将是这样的:

{
  "weightLogId": 5,
  "dateOfLog": "2016-03-13",
  "weight": 87.5,
  "userId": 1,
  "change": 0
}

无论如何,我可以获得"名称"在ResultSetMapping中给出以显示在这里?

它实际上完全混淆了JSON格式,因为现在属性在对象中一层向下,现在与UI上的Backbone模型格式不匹配,即:

defaults: {
        weight: 0,
        dateOfLog: "2016-01-01",
        userId: 1,
        change: "0"
    },

作为参考,使用的ResultMapping是:

@SqlResultSetMapping(
    name = "WeightLogWithChange",
    entities = {
            @EntityResult(
                    entityClass = WeightLog.class,
                    fields = {
                            @FieldResult(name = "weightLogId", column = "weightLogId"),
                            @FieldResult(name = "userId", column = "userId"),
                            @FieldResult(name = "dateOfLog", column = "dateOfLog"),
                            @FieldResult(name = "weight", column = "weight")
                    }),
    },
    columns = @ColumnResult(name = "change", type = Double.class)

编辑选项2

对于选项二,命名查询将如何更改?

@NamedNativeQueries({
    @NamedNativeQuery(
            name = "WeightLog.getAll",
            query = "select weightLogId, dateOfLog, weight, weight - lag(weight) over (ORDER BY dateOfLog) as change, userId from weightLog order by dateOfLog desc",
            resultSetMapping = "WeightLogWithChange"
    )

})

这里包含滞后SQL。如果将Lag部分移动到属性上方的公式,那么此命名查询如何更改?我认为这里的滞后部分需要删除?

1 个答案:

答案 0 :(得分:2)

如果我理解正确,您希望change属性在执行查询时可见,但在删除/更新/插入操作中不可见。

解决方案1 ​​

一种方法是单独处理此参数,而不是作为实体属性,但仍使其靠近采用 JPA 功能的实体,@SqlResultSetMappingdocumentation

您的实体将使用新的映射注释(忽略所有其他注释)看起来像这样:

@SqlResultSetMapping(
   name = "WeightLogWithChange",
   entityClass = WeightLog.class,
   fields = {
      @FieldResult(name = "id", column = "weightLogId"),
      @FieldResult(name = "userId", column = "userId"),
      @FieldResult(name = "someDate", column = "dateOfLog"),
      @FieldResult(name = "weight", column = "weight")}),
   columns = @ColumnResult(name = "change", type = Double.class))
class WeightLog {
  //..
}

然后调用Native查询:

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

List<Object[]> results = em.createNativeQuery("select weightLogId, dateOfLog, weight, weight - lag(weight) over (ORDER BY dateOfLog) as change, userId from weightLog order by dateOfLog desc", "WeightLogWithChange").getResultList();

results.stream().forEach((result) ->  {
   log.info("This is my weightLog object:" + result[0] + " and this is my computed change stored in a separate var: " + result[1]);
});

em.getTransaction().commit();
em.close();

在上面的例子中注意:1)我们如何将SQL的返回列值映射到实体属性,将change变量映射到我们想要的特定类型,2)我们如何从本机查询中获取结果作为一个数组,其中第一个元素是返回的实体对象,第二个是我们映射的变量change

解决方案2

您可以将change保留在实体中,并将​​其标记为公式,这是 Hibernate 特定功能。

来自documentation

  

强大的功能是派生属性。这些属性是由   只读定义。属性值在加载时计算。您   将计算声明为SQL表达式。然后转换为   SQL查询中的SELECT子句子查询,用于加载实例。

在这种情况下,属性看起来像:

//you have to check the validity of the SQL
@Formula("(select weight - lag(weight) over (ORDER BY dateOfLog) from weightLog wl where wl.id = id)")
private Double change;