为JPA中的列设置默认值

是否可以为JPA中的列设置默认值,如果可以,如何使用注释来完成?

501499 次浏览

这在JPA中是不可能的。

下面是你可以对Column注释所做的

列注释不能这样做。我认为唯一的方法是在创建对象时设置默认值。也许默认构造函数是这样做的正确位置。

实际上,这在JPA中是可能的,尽管使用@Column注释的columnDefinition属性需要一点hack,例如:

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

您可以执行以下操作:

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

在那里!您只是使用了0作为默认值。

注意,如果您只从该应用程序访问数据库,则此方法将适用。如果其他应用程序也使用该数据库,则应该使用卡梅隆的 columnDefinition注释属性或其他方式从数据库进行检查。

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

我刚刚测试了这个问题。谢谢你的提示。


关于评论:

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

这个设置了数据库中的默认列值。

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自动装箱在这方面帮助很大。

JPA和Hibernate注释都不支持默认列值的概念。作为解决这个限制的方法,在调用Hibernate save()update()会话之前设置所有默认值。这尽可能地(除了Hibernate设置默认值之外)模拟了数据库在保存表中的一行时设置默认值的行为。

选择答案所建议的在模型类中设置默认值不同,这种方法还确保使用Example对象作为搜索原型的条件查询将继续像以前一样工作。当您在模型类中设置一个可为空的属性(具有非基本类型的属性)的默认值时,Hibernate示例查询将不再忽略相关的列,以前它会因为它为空而忽略它。

鉴于我在试图解决相同的问题时无意中从谷歌中发现了这个问题,我只是要抛出我炮制的解决方案,以防有人发现它有用。

在我看来,这个问题只有一种解决方案——@PrePersist。如果你在@PrePersist中这样做,你必须检查这个值是否已经设置了。

在我的例子中,我修改了休眠核源代码,好吧,引入了一个新的注释@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 );

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

我使用columnDefinition和它的工作非常好

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")


private Date createdDate;

如果你正在使用double,你可以使用以下语句:

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


private Double grolsh;

是的,它是特定于db的。

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

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

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

你可以使用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();
}
}
}

在2017年,JPA 2.1仍然只有@Column(columnDefinition='...'),您可以将列的文字SQL定义放入其中。这是相当不灵活的,并迫使您还声明其他方面,如类型,从而缩短了JPA实现在这方面的视图。

Hibernate有这个:

@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类成员来替代。

  1. 当你在插入数据时在数据库中设置默认约束时,@Column(columnDefinition='...')将不起作用。
  2. 您需要使insertable = false并从注释中删除columnDefinition='...',然后数据库将自动从数据库中插入默认值。
  3. 例如,当你在数据库中设置varchar时,性别默认为男性。
  4. 你只需要在Hibernate/JPA中添加insertable = false,它就可以工作了。
@PrePersist
void preInsert() {
if (this.dateOfConsent == null)
this.dateOfConsent = LocalDateTime.now();
if(this.consentExpiry==null)
this.consentExpiry = this.dateOfConsent.plusMonths(3);
}

在我的情况下,由于字段是LocalDateTime我使用这个,建议由于供应商独立性

我找到了另一种方法来解决同样的问题,因为当我创建自己的对象并在数据库中持久化时,不尊重DDL的默认值。

因此,我查看了控制台,生成了SQL,并看到所有字段都有插入,但对象中只有一个属性的值发生了变化。

我在模型类中放了这个annotation。

@DynamicInsert

插入数据时,框架不会插入空值或未修改的值,从而使插入更短。

也有@DynamicUpdate注释。

我尝试了一些JPA/ hibernate的方法,但没有一个似乎工作得很好。因为我使用的是Oracle,所以我在触发器之前创建了一个"在触发器内简单测试空值,然后根据需要设置空值

@ColumnDefault("abcd")
var name: String,

在那里!您已经为列名设置了默认值