是否可以为 JPA 中的列设置默认值,如果使用注释,它是如何完成的?
19 回答
您可以执行以下操作:
@Column(name="price")
private double price = 0.0;
那里!您刚刚使用零作为默认值。
请注意,如果您仅从此应用程序访问数据库,这将为您服务。如果其他应用程序也使用数据库,那么您应该使用Cameron 的 columnDefinition注释属性或其他方式从数据库中进行此检查。
columnDefinition
实际上,在 JPA 中是可能的,尽管使用注释的属性有点小技巧@Column
,例如:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
另一种方法是使用 javax.persistence.PrePersist
@PrePersist
void preInsert() {
if (this.createdTime == null)
this.createdTime = new Date();
}
在 2017 年,JPA 2.1 仍然只有@Column(columnDefinition='...')
您在其中放置列的文字 SQL 定义。这是非常不灵活的,并迫使您还声明类型等其他方面,从而缩短了 JPA 实现对此事的看法。
休眠虽然,有这个:
@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;
标识通过 DDL 应用于关联列的 DEFAULT 值。
- Hibernate 4.3 文档(4.3 表明它已经存在了很长一段时间)
- 关于数据库列的默认值的休眠手册
对此有两点说明:
1)不要害怕不标准。作为一名 JBoss 开发人员,我见过不少规范流程。该规范基本上是给定领域的大玩家愿意承诺在未来十年左右提供支持的基线。对于安全来说确实如此,对于消息传递来说,ORM 没有区别(尽管 JPA 涵盖了很多内容)。我作为开发人员的经验是,在一个复杂的应用程序中,迟早你会需要一个非标准的 API。并且@ColumnDefault
是一个例子,它超过了使用非标准解决方案的负面影响。
2)每个人都如何挥动@PrePersist 或构造函数成员初始化,这很好。但这不一样。批量 SQL 更新怎么样?不设置列的语句怎么样?DEFAULT
有它的作用,并且不能通过初始化 Java 类成员来替代。
JPA 不支持这一点,如果支持它会很有用。使用 columnDefinition 是特定于数据库的,在许多情况下是不可接受的。当您检索具有空值的记录时,在类中设置默认值是不够的(这通常发生在您重新运行旧的 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 自动装箱在这方面有很大帮助。
@Column(columnDefinition="tinyint(1) default 1")
我刚刚测试了这个问题。它工作得很好。感谢您的提示。
关于评论:
@Column(name="price")
private double price = 0.0;
这个没有设置数据库中的默认列值(当然)。
看到我在尝试解决同样的问题时从谷歌偶然发现了这个,我只是将我准备好的解决方案扔进去,以防有人发现它有用。
从我的角度来看,这个问题实际上只有 1 个解决方案——@PrePersist。如果您在@PrePersist 中执行此操作,则必须检查该值是否已经设置。
你可以使用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();
}
}
}
我用过columnDefinition
,效果很好
@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date createdDate;
您不能使用列注释来执行此操作。我认为唯一的方法是在创建对象时设置默认值。也许默认构造函数是这样做的正确位置。
就我而言,我修改了 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 );
好吧,这是一个仅限休眠的解决方案。
@Column(columnDefinition='...')
插入数据时在数据库中设置默认约束时不起作用。- 您需要从注释中创建
insertable = false
和删除columnDefinition='...'
,然后数据库将自动从数据库中插入默认值。 - 例如,当您在数据库中设置 varchar 时,性别默认为男性。
- 您只需要添加
insertable = false
Hibernate/JPA 即可。
@PrePersist
void preInsert() {
if (this.dateOfConsent == null)
this.dateOfConsent = LocalDateTime.now();
if(this.consentExpiry==null)
this.consentExpiry = this.dateOfConsent.plusMonths(3);
}
在我的情况下,由于字段是 LocalDateTime 我使用了这个,由于供应商独立性,建议使用它
这在 JPA 中是不可能的。
以下是您可以使用 Column 注释执行的操作:http: //java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
如果您使用的是双打,您可以使用以下内容:
@Column(columnDefinition="double precision default '96'")
private Double grolsh;
是的,它是特定于数据库的。
JPA 和 Hibernate 注释都不支持默认列值的概念。作为此限制的解决方法,请在调用 Hibernate 之前save()
或update()
在会话上设置所有默认值。这尽可能地(没有 Hibernate 设置默认值)模仿数据库的行为,当它在表中保存一行时设置默认值。
与此替代答案所建议的在模型类中设置默认值不同,此方法还确保使用Example
对象作为搜索原型的条件查询将继续像以前一样工作。当您在模型类中设置可空属性(具有非原始类型的属性)的默认值时,Hibernate 示例查询将不再忽略之前因为它为空而忽略它的关联列。
您可以在数据库设计器中定义默认值,也可以在创建表时定义。例如,在 SQL Server 中,您可以将 Date 字段的默认 Vault 设置为 ( getDate()
)。insertable=false
按照列定义中的说明使用。JPA 不会在插入时指定该列,数据库将为您生成值。
我找到了解决相同问题的另一种方法,因为当我创建自己的对象并保存在数据库中并且不尊重具有默认值的 DDL 时。
所以我查看了我的控制台,生成了 SQL,并看到 insert 带有所有字段,但我的对象中只有一个属性的值发生了变化。
所以我在模型类中放了这个注解。
@DynamicInsert
插入数据时,框架不插入空值或未修改的值,使插入时间更短。
还有@DynamicUpdate
注释。
我尝试了几种 JPA/Hiberate 方法,但似乎没有一种效果很好。由于我使用的是 Oracle,因此我在触发器中创建了一个“前触发器”,对 null 进行简单测试,然后根据需要设置 null