【问题标题】:Avoid insert 'null' values to database table via JPA避免通过 JPA 将“null”值插入数据库表
【发布时间】:2011-05-29 08:39:40
【问题描述】:

我从 JPA2 开始,到目前为止感觉很舒服。但是在为具有默认值的 NON NULL 数据库字段保留具有 null 属性值的实体时,我遇到了问题。

我希望能够将实体属性保留为空并让数据库插入默认值。

我当前的设置是带有 PostgreSQL 的 openJPA。

我有这个 VERSION 数据库表(Vorgabewert = 默认值):

Spalte | Typ | Attribute ----------------+-----------------------------+---------------------------- status_ | smallint | not null Vorgabewert 0 time_ | timestamp without time zone | not null system_time | timestamp without time zone | not null Vorgabewert now() version | character varying(20) | not null activationtime | timestamp without time zone | importtime | timestamp without time zone |

我有一个实体 (Java DTO),它通过 xml 配置映射数据库字段(“状态”除外)。

我希望我可以插入一个没有设置system_time 的实体,并希望数据库将当前时间填充为默认值。

JPA 构造以下 SQL 查询:

INSERT INTO public.version (version, activationtime, importtime, system_time, time_) VALUES (?, ?, ?, ?, ?) [params=?, ?, ?, ?, ?]

Postgres 的反应是:

FEHLER: NULL-Wert in Spalte »system_time« verletzt Not-Null-Constraint(对不起德语,但此消息表示“system_time”上的 Not-Null-Constraint 违规)。

那我该怎么办?这是 JPA 还是数据库问题。 我可以将 JPA 配置为从 INSERT SQL 语句中排除空属性吗?

我希望能够在我的实体中设置“system_time”或让它为“null”并让数据库放置默认值。

欢迎任何帮助!

问候 克劳斯

【问题讨论】:

    标签: java postgresql orm jpa jpa-2.0


    【解决方案1】:

    我不会将数据库中的默认值与 JPA 结合使用。您必须在插入后回读实体,否则实体状态和数据库状态之间不匹配。

    这里选择务实的方法,初始化java中的所有值。从未听说过告诉 JPA/Hibernate 在插入/更新中省略空值的方法。

    【讨论】:

    • 好点 - 我希望持久化对象并将其置于“托管”状态将包括一种回读操作。
    • 接受了这个答案,因为重要的“状态不匹配”注释。答案还指出,没有办法做我想做的事情:-(。
    【解决方案2】:

    使用注解

    @Column(insertable = false)
    

    将阻止在 sql 中生成值。

    【讨论】:

    • 提示 +1 - 但这也可以防止生成非空值。在这种情况下,我想在 SQL 语句中有值。似乎没有办法实现这种行为..
    • 啊,我明白了。没有办法做到这一点。您必须决定是由 Java 还是 DBMS 负责时间,您不能同时拥有两者。
    • 您是否可以将两个实体映射到同一个表,其中一个将列定义为insertable=false,而另一个没有,通过使用一个或另一个来控制自动生成?我只是出于理论上的兴趣才问这个问题——这显然是一件很疯狂的事情。
    • 我希望 Hibernate 对这类事情进行健全性检查,尽管我从未尝试过。也许如果您同时使用两个完全独立的 Hibernate 实例......
    • 还有 updatable = false 从更新语句中删除该列。不是 OP 所要求的,而是相关的
    【解决方案3】:

    在注释 @Column 中将属性 insertable 设置为 false,如下所示:

    `@Column(name="system_time", insertable = false)`

    或者,如果您需要检查该值何时为 NULL,则在表上插入前触发。

    【讨论】:

      【解决方案4】:

      来自文档:columnDefinition:为列生成 DDL 时使用的 SQL 片段。

      通过使用columnDefinition,您可以根据需要指定约束。

         @Column(name="COLUMN_NAME", 
         columnDefinition="DATE DEFAULT CURRENT_DATE",table="TABLE_NAME")
      

      否则,您可以尝试在实体本身中初始化字段以摆脱这种情况。

          @Column(name = "somedate", nullable = false)
          private Date someDate = new Date();
      

      因此,如果您不设置,默认情况下会插入当前日期。

      【讨论】:

      • 正如您所引用的,此定义仅在为列生成 DDL 时使用(用于创建表)。因此,在运行应用程序时,它对 SQL 语句中的空值没有影响(尝试在此列中插入 NULL 时我仍然遇到违规行为)。这也发生在普通 SQL 中,所以我正在 JPA 中寻找一种配置方式,以便在生成的(INSERT)SQL 语句为空时排除列。
      • 默认值仍然无济于事,因为只有在 INSERT INTO 子句中没有明确列出列时才会使用默认值
      • 初始化默认值肯定会有所帮助 - 但如果某些进程将值设置回 NULL,则插入将失败。
      • 可以在实体的setter-method中配置,只有不为null时才设置值。
      • 是否可以在列定义或其他方式中指定约束名称。如果是,请说明。谢谢
      【解决方案5】:

      我找到了一个简单的解决方案: 在 pojo 中定义一个默认值。

      @Column(name="system_time")
      private Date systemTime = new Date();
      

      【讨论】:

        【解决方案6】:

        要在 hibernate insert sql stmt 中排除 null 属性值,请使用属性 dynamic-insert=true

        1.@org.hibernate.annotations.Entity(dynamicInsert = true) 在类上添加这个 2.在xml映射中,在class标签中使用dynamic-insert=true属性。

        【讨论】:

          【解决方案7】:

          对于那些将 Spring Framework 与 JPA 一起使用的人,请查看本文的第 4 节以获得一个优雅的解决方案。 所有功劳归于 BAELDUNG

          https://www.baeldung.com/database-auditing-jpa

          注意:粘贴文章摘录以保留内容

          Spring Data JPA 是一个框架,它通过在 JPA 提供者的顶部添加一个额外的抽象层来扩展 JPA。该层允许通过扩展 Spring JPA 存储库接口来支持创建 JPA 存储库。

          出于我们的目的,您可以扩展 CrudRepository,即通用 CRUD 操作的接口。一旦您创建了存储库并将其注入另一个组件,Spring Data 就会自动提供实现,您就可以添加审计功能了。

          4.1。启用 JPA 审计 首先,我们希望通过注释配置启用审计。为此,只需在您的 @Configuration 类中添加 @EnableJpaAuditing:

          @Configuration
          @EnableTransactionManagement
          @EnableJpaRepositories
          @EnableJpaAuditing
          public class PersistenceConfig { ... }
          

          4.2。添加 Spring 的实体回调监听器 我们已经知道,JPA 提供了@EntityListeners 注解来指定回调监听器类。 Spring Data 提供了自己的 JPA 实体监听器类:AuditingEntityListener。所以让我们为 Bar 实体指定监听器:

          @Entity
          @EntityListeners(AuditingEntityListener.class)
          public class Bar { ... }
          

          现在,监听器将在持久化和更新 Bar 实体时捕获审计信息。

          4.3。跟踪创建日期和最后修改日期 接下来,我们将添加两个新属性,用于将创建日期和最后修改日期存储到我们的 Bar 实体中。属性由 @CreatedDate 和 @LastModifiedDate 注释相应地注释,并且它们的值是自动设置的:

          @Entity
          @EntityListeners(AuditingEntityListener.class)
          public class Bar {
              
              //...
              
              @Column(name = "created_date", nullable = false, updatable = false)
              @CreatedDate
              private long createdDate;
          
              @Column(name = "modified_date")
              @LastModifiedDate
              private long modifiedDate;
              
              //...
              
          }
          

          通常,您会将属性移动到一个基类(由@MappedSuperClass 注释),该基类将由您的所有审计实体进行扩展。在我们的示例中,为了简单起见,我们将它们直接添加到 Bar。

          4.4。使用 Spring Security 审计更改的作者 如果您的应用使用 Spring Security,您不仅可以跟踪更改的时间,还可以跟踪更改的人员:

          @Entity
          @EntityListeners(AuditingEntityListener.class)
          public class Bar {
              
              //...
              
              @Column(name = "created_by")
              @CreatedBy
              private String createdBy;
          
              @Column(name = "modified_by")
              @LastModifiedBy
              private String modifiedBy;
              
              //...
              
          }
          

          使用 @CreatedBy 和 @LastModifiedBy 注释的列填充有创建或最后修改实体的主体的名称。该信息是从 SecurityContext 的 Authentication 实例中提取的。如果您想自定义设置为注释字段的值,您可以实现 AuditorAware 接口:

          public class AuditorAwareImpl implements AuditorAware<String> {
           
              @Override
              public String getCurrentAuditor() {
                  // your custom logic
              }
          
          }
          

          为了将应用程序配置为使用 AuditorAwareImpl 来查找当前主体,请声明使用 AuditorAwareImpl 实例初始化的 AuditorAware 类型的 bean,并在 @EnableJpaAuditing 中将 bean 的名称指定为 auditAwareRef 参数的值:

          @EnableJpaAuditing(auditorAwareRef="auditorProvider")
          public class PersistenceConfig {
              
              //...
              
              @Bean
              AuditorAware<String> auditorProvider() {
                  return new AuditorAwareImpl();
              }
              
              //...
              
          }
          

          【讨论】:

            【解决方案8】:

            我使用这个(针对 Oracle 数据库):

            @Temporal(TemporalType.TIMESTAMP)
            @Column(name="CREATION_TIME", 
                    columnDefinition="TIMESTAMP DEFAULT SYSTIMESTAMP",
                    nullable=false,
                    insertable=false,
                    updatable=false)
            private Date creationTime;
            

            【讨论】:

              【解决方案9】:

              您可以通过在 @column 注释中添加 nullable=false 来避免 null 属性。像这样

              @Column(name = "muColumn", nullable = false)
              

              how to avoid the null property in insert or update

              【讨论】:

                【解决方案10】:

                只需从 INSERT 语句的列中省略 system_time。

                【讨论】:

                • INSERT 语句由 JPA EntityManager 在持久化实体 (javax.persistence.EntityManager.persist(Object)) 时生成。如果为空,如何配置 JPA 以省略该列?
                • 不确定 JPA,但 Hibernate 支持注释“dynamicInsert = true, dynamicUpdate = true”,它不会发送任何未更改的行。
                • Hibernate 还支持属性映射上的“insert='false'”和“update='false'”,以从生成的静态插入/更新 SQL 中省略某些列。在这里映射“insert='false' generated='insert'”可能是合适的(虽然不确定如何将其转换为注释配置)。
                【解决方案11】:

                我不知道在 JPA 中有什么方法可以做到这一点。

                但是您可以向数据库添加一个触发器,该触发器会捕获将 NULL 插入相关列的尝试,并重写插入以插入默认值?本质上,您将默认生成行为完全下移到数据库中,而不是在 JPA 和数据库之间拆分它。

                但是,正如 bert 在他的回答中指出的那样,这会使对象和数据库不一致,这几乎可以肯定是一个糟糕的想法。

                【讨论】:

                  【解决方案12】:

                  你可以这样使用:

                  @JoinColumn(name = "team_id", nullable = false)
                  

                  使用这种方式,JPA 将检查NULL 值。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-10-06
                    • 2016-03-22
                    • 2016-04-11
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-07-23
                    • 1970-01-01
                    相关资源
                    最近更新 更多