避免插入“null”通过 JPA 将值写入数据库表

发布于 2024-10-08 21:54:00 字数 1334 浏览 2 评论 0原文

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

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

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

我有这个版本数据库表(Vorgaweert = 默认值):


     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 (抱歉德语,但此消息意味着 Not-Null-违反“system_time”的约束)。

那我能做什么呢?这是 JPA 还是数据库问题? 我可以配置 JPA 以从 INSERT SQL 语句中排除 null 属性吗?

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

欢迎任何帮助!

问候 克劳斯

im starting with JPA2 and feel quite comfortbale so far. But I have a problem when persisting Entities with null property values for NON NULL database fields with default value.

I would like to be able to leave the entity property null and let the database insert the default value.

My current setup is openJPA with PostgreSQL.

I have this VERSION database table (Vorgabewert = Default value):


     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 |

I have an entity (Java DTO) which maps the database fields (except 'status') by xml configuration.

I hoped I could insert an entity without the system_time set and expected that the database will fill the current time as default value.

JPA constructs the following SQL-Query:

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

and Postgres reacts with:

FEHLER: NULL-Wert in Spalte »system_time« verletzt Not-Null-Constraint (sorry for German language but this message means the Not-Null-Constraint violation on 'system_time').

So what can I do? Is this a JPA or Database Problem.
Can I configure JPA to exclude null properties from the INSERT SQL Statement.

I want to have the ability to set the 'system_time' in my entity or to let it be 'null' and let the database put the default value.

Any help is welcome!

Regads
Klaus

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

遗心遗梦遗幸福 2024-10-15 21:54:00

使用注释

@Column(insertable = false)

将阻止在 sql 中生成值。

Using the annotation

@Column(insertable = false)

will prevent the value being generated in the sql.

疧_╮線 2024-10-15 21:54:00

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

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

I would not rely on default values in the database in conjunction with JPA. You would have to read the entity back after the insert otherwise you have a mismatch between the entity state and the db state.

Choose the pragmatic approach here and initialise all values in java. Never heard of a way to tell JPA/Hibernate to leave out null values in an insert/update.

对不⑦ 2024-10-15 21:54:00

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

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

或者如果您需要检查值何时为 NULL,则在之前创建一个触发器插入桌子上。

In the Annotation @Column put the atribute insertable to false like this:

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

Or if you need check when the value is NULL then make a Trigger BEFORE INSERT on the table.

青芜 2024-10-15 21:54:00

来自文档: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();

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

From documentation : columnDefinition : The SQL fragment that is used when generating the DDL for the column.

By using columnDefinition, you can specify constraints as required.

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

Else you can try to initialize field in entity itself to get rid of this.

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

So by default current date will be inserted if you do not set it.

知足的幸福 2024-10-15 21:54:00

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

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

I found out a simple solution:
define a default value in the pojo.

@Column(name="system_time")
private Date systemTime = new Date();
澉约 2024-10-15 21:54:00

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

[email protected](dynamicInsert = true) 将其添加到课程中
2.在xml映射中,在class标签中使用dynamic-insert=true属性。

To exclude null property values in the hibernate insert sql stmt use attribute dynamic-insert=true.

[email protected](dynamicInsert = true) add this on the class
2.In xml mapping , use dynamic-insert=true attribute in class tag.

裂开嘴轻声笑有多痛 2024-10-15 21:54:00

对于那些使用 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,并指定bean 的名称作为@EnableJpaAuditing 中的auditorAwareRef 参数的值:

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

For those who use Spring Framework with JPA please have an look at section 4 of this article for an elegant solution. ALL CREDITS TO BAELDUNG

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

NB: Excerpt from article is pasted to preserve the content

Spring Data JPA is a framework that extends JPA by adding an extra layer of abstraction on the top of the JPA provider. This layer allows for support for creating JPA repositories by extending Spring JPA repository interfaces.

For our purposes, you can extend CrudRepository<T, ID extends Serializable>, the interface for generic CRUD operations. As soon as you've created and injected your repository to another component, Spring Data will provide the implementation automatically and you're ready to add auditing functionality.

4.1. Enabling JPA Auditing
To start, we want to enable auditing via annotation configuration. In order to do that, just add @EnableJpaAuditing on your @Configuration class:

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

4.2. Adding Spring's Entity Callback Listener
As we already know, JPA provides the @EntityListeners annotation to specify callback listener classes. Spring Data provides its own JPA entity listener class: AuditingEntityListener. So let's specify the listener for the Bar entity:

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

Now auditing information will be captured by the listener on persisting and updating the Bar entity.

4.3. Tracking Created and Last Modified Dates
Next, we will add two new properties for storing the created and last modified dates to our Bar entity. The properties are annotated by the @CreatedDate and @LastModifiedDate annotations accordingly, and their values are set automatically:

@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;
    
    //...
    
}

Generally, you would move the properties to a base class (annotated by @MappedSuperClass) which would be extended by all your audited entities. In our example, we add them directly to Bar for the sake of simplicity.

4.4. Auditing the Author of Changes With Spring Security
If your app uses Spring Security, you can not only track when changes were made but also who made them:

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

    @Column(name = "modified_by")
    @LastModifiedBy
    private String modifiedBy;
    
    //...
    
}

The columns annotated with @CreatedBy and @LastModifiedBy are populated with the name of the principal that created or last modified the entity. The information is pulled from SecurityContext‘s Authentication instance. If you want to customize values that are set to the annotated fields, you can implement AuditorAware interface:

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

}

In order to configure the app to use AuditorAwareImpl to look up the current principal, declare a bean of AuditorAware type initialized with an instance of AuditorAwareImpl and specify the bean's name as the auditorAwareRef parameter's value in @EnableJpaAuditing:

@EnableJpaAuditing(auditorAwareRef="auditorProvider")
public class PersistenceConfig {
    
    //...
    
    @Bean
    AuditorAware<String> auditorProvider() {
        return new AuditorAwareImpl();
    }
    
    //...
    
}
叶落知秋 2024-10-15 21:54:00

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

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

I use this (against an Oracle database):

@Temporal(TemporalType.TIMESTAMP)
@Column(name="CREATION_TIME", 
        columnDefinition="TIMESTAMP DEFAULT SYSTIMESTAMP",
        nullable=false,
        insertable=false,
        updatable=false)
private Date creationTime;
左秋 2024-10-15 21:54:00

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

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

如何避免插入或更新中的 null 属性

you can avoid the null property by add nullable=false to the @column annotation. like this

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

how to avoid the null property in insert or update

别靠近我心 2024-10-15 21:54:00

以下对我有用。假设您的模型中有一个布尔变量primaryContact,并且您希望在未指定或指定为 null 时将其设置为 false:

@PrePersist
public void prePersist() {
    if (primaryContact == null) {
        primaryContact = false;
    }
}

@PreUpdate
public void preUpdate() {
    if (primaryContact == null) {
        primaryContact = false;
    }
}

Following works for me. Assuming you have a Boolean variable primaryContact in your model and you want to set it to false when it is not specified or null is specified:

@PrePersist
public void prePersist() {
    if (primaryContact == null) {
        primaryContact = false;
    }
}

@PreUpdate
public void preUpdate() {
    if (primaryContact == null) {
        primaryContact = false;
    }
}
┾廆蒐ゝ 2024-10-15 21:54:00

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

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

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

I don't know of any way to do this in JPA.

But you could add a trigger to the database which catches attempts to insert NULL into the column in question, and rewrites the insert to insert the default? Essentially, you move the default-generation behaviour entirely down into the database, rather than splitting it between JPA and the database.

However, as bert points out in his answer, this would leave the object and the database inconsistent, which is almost certainly a bad think.

若水微香 2024-10-15 21:54:00

你可以使用这种方式:

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

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

you can use this way:

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

Using this way, JPA will check NULL value.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文