为JPA中的列设置默认值


Answers:


230

实际上,在JPA中是可能的,尽管使用批注的columnDefinition属性有些小小的@Column改动,例如:

@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")

3
10是整数部分,2是小数部分,因此:1234567890.12是受支持的数字。
内森·费格

23
还要注意,此columnDefinition不一定与数据库无关,并且肯定不会自动与Java中的数据类型绑定。
内森·费格

47
创建具有空字段的实体并将其持久化后,您将无需设置值。不是解决方案...
Pascal Thivent 2010年

18
不@NathanFeger,长度为10,小数部分为2。因此1234567890.12将不是受支持的号码,但12345678.90是有效的。
GPrimola 2014年

4
您将需要insertable=false该列是否可为空(并避免使用不必要的column参数)。
eckes

314

您可以执行以下操作:

@Column(name="price")
private double price = 0.0;

那里!您刚刚使用零作为默认值。

请注意,如果您仅从此应用程序访问数据库,这将为您提供服务。如果其他应用程序也使用该数据库,则应使用Cameron的 columnDefinition批注属性或其他方式从数据库中进行此检查。


38
如果您希望实体在插入后具有正确的值,则这是正确的方法。+1
Pascal Thivent

16
在模型类中设置可为空的属性(具有非原始类型的属性)的默认值将破坏使用Example对象作为搜索原型的条件查询。设置默认值后,Hibernate示例查询将不再忽略关联的列,因为该列为空,因此以前该位置将忽略它。更好的方法是在调用Hibernate save()或之前设置所有默认值update()。这更好地模仿了数据库的行为,该行为在保存行时设置默认值。
德里克·马哈尔

1
@Harry:这是设置默认值的正确方法,除非您使用的条件查询使用示例作为搜索原型。
德里克·马哈尔

12
这不能确保为非原始类型(null例如设置)设置默认值。使用@PrePersist@PreUpdate是更好的选择恕我直言。
贾斯珀,

3
这是为列指定默认值的正确方法。columnDefinition属性不是独立于数据库的,并且@PrePersist在插入之前会覆盖您的设置,“默认值”是其他内容,如果未明确设置该值,则使用默认值。
UtkuÖzdemir2014年

110

另一种方法是使用javax.persistence.PrePersist

@PrePersist
void preInsert() {
   if (this.createdTime == null)
       this.createdTime = new Date();
}

1
我使用了这样的方法来添加和更新时间戳。效果很好。
斯宾娜

10
这不应该是if (createdt != null) createdt = new Date();什么吗?现在,它将覆盖显式指定的值,这似乎使其实际上不是默认值。
Max Nanasy 2014年

16
@MaxNanasyif (createdt == null) createdt = new Date();
Shane

客户端和数据库位于不同时区是否重要?我想象使用诸如“ TIMESTAMP DEFAULT CURRENT_TIMESTAMP”之类的东西将获得数据库服务器上的当前时间,而“ createdt = new Date()”将获得该Java代码中的当前时间,但是如果这两个时间不相同,客户端正在不同时区连接到服务器。
FrustratedWithFormsDesigner 2015年

添加了null检查。
Ondra参观Žižka

49

在2017年,JPA 2.1仍然只有@Column(columnDefinition='...')您将列的文字SQL定义放入其中。这是非常不灵活的,迫使您也声明其他方面,例如类型,从而使JPA实现对此的观点短路。

休眠虽然有:

@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;

标识要通过DDL应用于关联列的DEFAULT值。

有两个注意事项:

1)不要害怕变得不合标准。作为JBoss开发人员,我已经看到了很多规范过程。该规范基本上是给定领域的大型企业愿意在未来十年左右支持的基线。对于安全性而言,对于消息传递而言,ORM没什么不同(尽管JPA涵盖了很多内容)。我作为开发人员的经验是,在复杂的应用程序中,迟早您还是需要一个非标准的API。这@ColumnDefault是一个克服使用非标准解决方案的缺点的例子。

2)大家如何挥动@PrePersist或构造函数成员初始化,这很不错。但这不一样。批量SQL更新如何?没有设置列的语句怎么样?DEFAULT具有它的作用,并且不能通过初始化Java类成员来替代。


我可以在Wildfly 12中使用什么版本的休眠注释,但在特定版本中遇到错误?感谢ind提前!
Fernando Pie

谢谢恩德拉。2019年还有其他解决方案吗?
艾伦(Alan)

1
遗憾的是,@ Alan没有库存的JPA。
jwenting

13

JPA不支持该功能,如果支持,它将很有用。使用columnDefinition是特定于DB的,在许多情况下不可接受。当检索具有空值的记录时(通常在重新运行旧的DBUnit测试时发生),在类中设置默认值是不够的。我要做的是:

public class MyObject
{
    int attrib = 0;

    /** Default is 0 */
    @Column ( nullable = true )
    public int getAttrib()

    /** Falls to default = 0 when null */
    public void setAttrib ( Integer attrib ) {
       this.attrib = attrib == null ? 0 : attrib;
    }
}

Java自动装箱在这方面大有帮助。


不要滥用二传手!它们用于在对象字段中设置参数。而已!最好使用默认构造函数作为默认值。
罗兰

@Roland理论上不错,但是在处理已经包含空值的现有数据库(应用程序不希望包含空值)的现有数据库时,它并不总是起作用。您需要先进行数据库转换,在该转换中,您应使列不为空,并同时设置合理的默认值,然后处理其他应用程序的强烈反对,认为它实际上是可为空的(也就是说,如果您甚至可以修改数据库)。
jwenting

如果涉及到#JPA和setter / getter,则它们必须始终是纯setter / getter,否则有一天您的应用程序将变得越来越多,并成为维护的噩梦。最好的建议是在这里接吻(我不是同性恋,哈哈)。
罗兰

10

看到我在尝试解决同一问题时偶然发现来自Google的内容时,我会提出自己准备的解决方案,以防有人发现它有用。

从我的角度来看,对于此问题,实际上只有一种解决方案-@PrePersist。如果在@PrePersist中执行此操作,则必须检查该值是否已设置。


4
+1-我肯定会选择@PrePersistOP的用例。@Column(columnDefinition=...)看起来不是很优雅。
Piotr Nowicki 2012年

如果存储中已经有空值的数据,@ PrePersist不会为您提供帮助。它不会为您神奇地进行数据库转换。
jwenting

10
@Column(columnDefinition="tinyint(1) default 1")

我刚刚测试了这个问题。它工作正常。感谢您的提示。


关于评论:

@Column(name="price") 
private double price = 0.0;

当然,这并没有设置数据库中的默认列值。


9
在对象级别上无法正常运行(在实体中插入后,您将无法获得数据库的默认值)。Java级别的默认值。
Pascal Thivent

它可以工作(特别是如果您在数据库中指定了insertable = false,但是不幸的是,缓存的版本没有刷新(即使JPA选择了表和列也没有刷新)。至少不与hibernate:-/一起使用,即使它可以工作,往返是不好的
eckes

9

您可以使用Java反射api:

    @PrePersist
    void preInsert() {
       PrePersistUtil.pre(this);
    }

这很常见:

    public class PrePersistUtil {

        private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");


        public static void pre(Object object){
            try {
                Field[] fields = object.getClass().getDeclaredFields();
                for(Field field : fields){
                    field.setAccessible(true);
                    if (field.getType().getName().equals("java.lang.Long")
                            && field.get(object) == null){
                        field.set(object,0L);
                    }else if    (field.getType().getName().equals("java.lang.String")
                            && field.get(object) == null){
                        field.set(object,"");
                    }else if (field.getType().getName().equals("java.util.Date")
                            && field.get(object) == null){
                        field.set(object,sdf.parse("1900-01-01"));
                    }else if (field.getType().getName().equals("java.lang.Double")
                            && field.get(object) == null){
                        field.set(object,0.0d);
                    }else if (field.getType().getName().equals("java.lang.Integer")
                            && field.get(object) == null){
                        field.set(object,0);
                    }else if (field.getType().getName().equals("java.lang.Float")
                            && field.get(object) == null){
                        field.set(object,0.0f);
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

2
我喜欢这种解决方案,但有一点缺点,我认为在设置默认值之前检查列是否可为空是很重要的。
路易斯·卡洛斯

如果您愿意,也可以将代码从头Field[] fields = object.getClass().getDeclaredFields();插入for()。并添加final到您的参数/捕获的异常中,因为您不想object被意外修改。还要添加一个检查nullif (null == object) { throw new NullPointerException("Parameter 'object' is null"); }。这样可以确保object.getClass()调用安全并且不会触发NPE。原因是为了避免懒惰的程序员犯错误。;-)
罗兰

7

我使用columnDefinition,效果很好

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;

6
看起来这使您的jpa实现供应商特定。
乌度(Udo)在2012年

它仅使DDL供应商特定。但是无论如何,您都有可能是特定于供应商的DDL黑客。
eckes

7

您不能使用列注释来执行此操作。我认为唯一的方法是在创建对象时设置默认值。也许默认的构造函数将是正确的选择。


好主意。可悲的是,@Column周围没有通用的注释或属性。而且我也想念被设置的注释(取自Java doctag)。
罗兰

5

就我而言,我修改了hibernate-core源代码,以引入一个新的注释@DefaultValue

commit 34199cba96b6b1dc42d0d19c066bd4d119b553d5
Author: Lenik <xjl at 99jsj.com>
Date:   Wed Dec 21 13:28:33 2011 +0800

    Add default-value ddl support with annotation @DefaultValue.

diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/DefaultValue.java b/hibernate-core/src/main/java/org/hibernate/annotations/DefaultValue.java
new file mode 100644
index 0000000..b3e605e
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/DefaultValue.java
@@ -0,0 +1,35 @@
+package org.hibernate.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Specify a default value for the column.
+ *
+ * This is used to generate the auto DDL.
+ *
+ * WARNING: This is not part of JPA 2.0 specification.
+ *
+ * @author 谢继雷
+ */
+@java.lang.annotation.Target({ FIELD, METHOD })
+@Retention(RUNTIME)
+public @interface DefaultValue {
+
+    /**
+     * The default value sql fragment.
+     *
+     * For string values, you need to quote the value like 'foo'.
+     *
+     * Because different database implementation may use different 
+     * quoting format, so this is not portable. But for simple values
+     * like number and strings, this is generally enough for use.
+     */
+    String value();
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
index b289b1e..ac57f1a 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java
@@ -29,6 +29,7 @@ import org.hibernate.AnnotationException;
 import org.hibernate.AssertionFailure;
 import org.hibernate.annotations.ColumnTransformer;
 import org.hibernate.annotations.ColumnTransformers;
+import org.hibernate.annotations.DefaultValue;
 import org.hibernate.annotations.common.reflection.XProperty;
 import org.hibernate.cfg.annotations.Nullability;
 import org.hibernate.mapping.Column;
@@ -65,6 +66,7 @@ public class Ejb3Column {
    private String propertyName;
    private boolean unique;
    private boolean nullable = true;
+   private String defaultValue;
    private String formulaString;
    private Formula formula;
    private Table table;
@@ -175,7 +177,15 @@ public class Ejb3Column {
        return mappingColumn.isNullable();
    }

-   public Ejb3Column() {
+   public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    public Ejb3Column() {
    }

    public void bind() {
@@ -186,7 +196,7 @@ public class Ejb3Column {
        }
        else {
            initMappingColumn(
-                   logicalColumnName, propertyName, length, precision, scale, nullable, sqlType, unique, true
+                   logicalColumnName, propertyName, length, precision, scale, nullable, sqlType, unique, defaultValue, true
            );
            log.debug( "Binding column: " + toString());
        }
@@ -201,6 +211,7 @@ public class Ejb3Column {
            boolean nullable,
            String sqlType,
            boolean unique,
+           String defaultValue,
            boolean applyNamingStrategy) {
        if ( StringHelper.isNotEmpty( formulaString ) ) {
            this.formula = new Formula();
@@ -217,6 +228,7 @@ public class Ejb3Column {
            this.mappingColumn.setNullable( nullable );
            this.mappingColumn.setSqlType( sqlType );
            this.mappingColumn.setUnique( unique );
+           this.mappingColumn.setDefaultValue(defaultValue);

            if(writeExpression != null && !writeExpression.matches("[^?]*\\?[^?]*")) {
                throw new AnnotationException(
@@ -454,6 +466,11 @@ public class Ejb3Column {
                    else {
                        column.setLogicalColumnName( columnName );
                    }
+                   DefaultValue _defaultValue = inferredData.getProperty().getAnnotation(DefaultValue.class);
+                   if (_defaultValue != null) {
+                       String defaultValue = _defaultValue.value();
+                       column.setDefaultValue(defaultValue);
+                   }

                    column.setPropertyName(
                            BinderHelper.getRelativePath( propertyHolder, inferredData.getPropertyName() )
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
index e57636a..3d871f7 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
@@ -423,6 +424,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
                getMappingColumn() != null ? getMappingColumn().isNullable() : false,
                referencedColumn.getSqlType(),
                getMappingColumn() != null ? getMappingColumn().isUnique() : false,
+               null, // default-value
                false
        );
        linkWithValue( value );
@@ -502,6 +504,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
                getMappingColumn().isNullable(),
                column.getSqlType(),
                getMappingColumn().isUnique(),
+               null, // default-value
                false //We do copy no strategy here
        );
        linkWithValue( value );

好吧,这是一个仅休眠的解决方案。


2
尽管我确实感谢为开源项目做出积极贡献的人们的努力,但我对这个答案不以为然,因为JPA是任何OR / M之上的标准Java规范,OP要求使用JPA方法来指定默认值,并且您的补丁程序可以工作仅适用于休眠。如果这是NHibernate,其中没有关于持久性的超级部分规范(MS EF甚至不支持NPA),我会赞成这种补丁。事实是,与ORM的要求相比,JPA非常有限(一个例子:没有二级索引)。反正荣誉的功夫
USR-本地ΕΨΗΕΛΩΝ

这不会造成维护问题吗?每当休眠升级或每次全新安装时,都需要重做这些更改吗?
user1242321

由于问题是关于JPA的,甚至没有提到Hibernate,所以这并不能回答问题
Neil Stockton

5
  1. @Column(columnDefinition='...') 在插入数据时在数据库中设置默认约束时不起作用。
  2. 您需要从注解中insertable = false删除columnDefinition='...',然后数据库将自动从数据库中插入默认值。
  3. 例如,当您在数据库中默认设置varchar性别为男性时。
  4. 您只需要添加insertable = falseHibernate / JPA,它就会起作用。


如果您有时要设置其值,则不是一个好的选择。
Shihe Zhang

@ShiheZhang使用false不能让我设置值吗?
fvildoso

3
@PrePersist
void preInsert() {
    if (this.dateOfConsent == null)
        this.dateOfConsent = LocalDateTime.now();
    if(this.consentExpiry==null)
        this.consentExpiry = this.dateOfConsent.plusMonths(3);
}

就我而言,由于我使用的是LocalDateTime字段,因此由于供应商的独立性,建议使用


2

JPA和Hibernate批注都不支持默认列值的概念。要解决此限制,请在调用Hibernate之前save()update()在会话上设置所有默认值。这尽可能接近(缺少Hibernate设置默认值)模仿了数据库的行为,该行为是在表中保存一行时设置默认值的。

与该替代答案所建议的在模型类中设置默认值不同,此方法还确保将Example对象作为搜索原型的条件查询将继续像以前一样工作。当您在模型类中设置可为空的属性(具有非原始类型的属性)的默认值时,Hibernate的示例查询将不再忽略关联的列,因为以前它会为空,因此它将忽略该列。


在以前的解决方案中,作者menthioned ColumnDefault(“”)
nikolai.serdiuk

@ nikolai.serdiuk表示,在编写此答案多年后,才添加了ColumnDefault批注。在2010年是正确的,没有这样的注释(实际上没有注释,只有xml配置)。
jwenting

1

在JPA中这是不可能的。

这是您可以对Column注解执行的操作:http : //java.sun.com/javaee/5/docs/api/javax/persistence/Column.html


1
无法指定默认值似乎是JPA注释的严重缺陷。
德里克·马哈尔

2
JDO允许在其ORM定义中使用它,因此JPA应该将其包括在内……一天
user383680,2010年

正如Cameron Pope回答的那样,您绝对可以使用columnDefinition属性来实现。
IntelliData,2016年

@DerekMahar这不是JPA规范中的唯一不足。这是一个很好的规范,但并不完美
jwenting

1

如果您使用双打,则可以使用以下内容:

@Column(columnDefinition="double precision default '96'")

private Double grolsh;

是的,它是特定于数据库的。


0

您可以在数据库设计器中或在创建表时定义默认值。例如,在SQL Server中,您可以将日期字段的默认保管库设置为(getDate())。insertable=false按列定义中所述使用。JPA不会在插入内容上指定该列,数据库将为您生成值。


-2

您需要insertable=false@Column注释中。JPA将在插入数据库时​​忽略该列,并使用默认值。

看到这个链接:http : //mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html


2
然后,您将如何插入给定值@runtime?
Ashish Ratan

是的,那是真的。nullable=false将会失败SqlExceptionCaused by: java.sql.SQLException: Column 'opening_times_created' cannot be null。在这里,我忘了用设置“创建的”时间戳openingTime.setOpeningCreated(new Date())。这是保持一致性的一种好方法,但这是发问者没有问的问题。
罗兰
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.