【问题标题】:JPA Annotation @Column(insertable=false) is being ignored, why?JPA Annotation @Column(insertable=false) 被忽略,为什么?
【发布时间】:2019-04-16 02:21:30
【问题描述】:

我希望在调用save() 方法时忽略其中一个字段。该字段将由数据库自动填充并返回。它应被视为只读字段。

关注private Timestamp ts;字段:

@Entity
@Table(name = "time_series", schema = "ms")
@IdClass(Reading.class)
public class Reading implements Serializable {

   private static final long serialVersionUID = 1L;

   @Id
   @Column(name = "name", nullable = false)
   private String sensorName;

   @Id
   @Column(name = "ts", insertable = false, updatable = false)
   private Timestamp ts;

   @Column(name = "reading")
   private Double value;
   ...

如您所见,我在 @Column 注释内使用 insertable = false, updatable = false,因此我希望在幕后形成实际 SQL 时忽略 ts

@Override
@Transactional(readOnly = false)
public Reading save(Reading r) {        
    return readingRepository.save(r);
}

ReadingRepository 基本上是 Spring 的 CrudRepository 扩展,具有save(...) 方法。

当我使用 ts=null 保存 Reading 对象时,我收到来自 Postgres 的错误:

错误:“ts”列中的空值违反非空约束

因为 Spring Data 实际上并没有忽略基于 我从日志中看到的内容ts 字段:

insert into ms.time_series (ts, name, reading) values (NULL, 'sensor1', 10.0)

显然,我希望查询没有像这样的 ts

insert into ms.time_series (name, reading) values ('sensor1', 10.0)

为什么字段没有被忽略?

现在,如果您问我的数据库架构是否还可以,我会说可以。当我在没有 ts 的控制台中键入 SQL 查询时,一切都很好。我什至尝试了@Generated@GeneratedValue 注释。 Namets 都构成了表的主键,但是,如果我只将其中一个设为 PK 或添加额外的代理项,结果是相同的标识列。结果一样...

我是否忽略了某些东西,或者 Spring 框架中是否存在错误??我使用的是 Spring 5.1.2 和 SpringData 2.1.2

注意:如果我使用 @Transient 注释可以正确保留插入查询,但即使在读取/获取时,该字段也会被完全忽略。

非常感谢您对此提供的任何帮助!

【问题讨论】:

  • 在您的 Timestamp 字段上使用 @Transient 注释。
  • 如果它不可插入或不可更新,它怎么可能是null?另外你的映射很奇怪@IdClass怎么能和实体类一样?
  • @M.Deinum 我必须添加@IdClass,这样我才能在这里有两个@Ids。正如我所提到的,当我删除该错误仍然存​​在。
  • 我知道@IdClass 的用途,但它应该是一个单独的类,而不是指向实体。还需要插入未生成的@Id(您需要指定)。所以基本上你的映射是冲突的。您不想要生成的值,但不想将其插入为。所以hibernate需要做出决定。
  • @AngadBansode 请阅读我的帖子,我不能使用@Transient,因为我需要能够获取并读取该值

标签: java spring hibernate jpa spring-data-jpa


【解决方案1】:

如你所说

我从 Postgres 收到一个错误

如果您查看docs,它会指出:

从技术上讲,主键约束只是唯一约束和非空约束的组合。

对于多列主键也是如此(参见here

因此,如果ts 是数据库中主键的一部分(如@Id 所示),则根本不可能在该列中插入空值。

IMO Hibernate/Spring 与此无关

insert into ms.time_series (ts, name, reading) values (NULL, 'sensor1', 10.0)

应该等价于

insert into ms.time_series (name, reading) values ('sensor1', 10.0)

【讨论】:

  • 好吧,如果 Hibernate/Spring 与此无关,那么发送的 SQL 查询应该仍然没有“ts”。我还说我试图让“ts”不是主键的一部分,但错误仍然存​​在
  • CREATE TABLE ms.time_series ( ts TIMESTAMP(0) DEFAULT ('now'::text)::timestamp(0), name VARCHAR(255), reading DOUBLE PRECISION, PRIMARY KEY(ts, name) ); 在 SQL 控制台中我可以运行查询 insert into ms.time_series (name, reading) values ('sensor1', 10) 没问题
  • 为什么要现在创建时间戳,将其转换为文本以再次将其转换为时间戳?这背后的原因是什么。
  • @M.Deinum 我从某个地方复制粘贴了它,我会修复它,但我不相信这是问题的原因。问题是 Spring/Hibernate 发送了一个不正确的 SQL 查询,这可能是由于我不正确地使用注释或框架本身的错误造成的。
  • @M.Deinum 我已经尽力了。 @GeneratedValue@TableGenerator 的所有可能排列。我已经花了 3 天的时间,所以我也打开了这个帖子 stackoverflow.com/questions/53290636/… 变得非常绝望
【解决方案2】:

尝试在您的代码中使用GenericGeneratorGeneratedValue

添加所需的注解并为 Reading 类中的所有其他成员赋值,ts 除外。

这里有一些examples

【讨论】:

  • 谢谢!我试过了,但没有用,也许我还没有想出正确的选择排列。
  • 无论如何,将 javax.persistence.* org.hibernate.annotations.* 注释混合在一起是个好主意吗? GeneratedValue 是 javax,GenericGenerator 是休眠状态。
猜你喜欢
  • 2011-04-17
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
  • 2021-11-06
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多