可以为JPA中的列设置默认值吗?如果使用注释,如何做到这一点?
可以为JPA中的列设置默认值吗?如果使用注释,如何做到这一点?
Answers:
实际上,在JPA中是可能的,尽管使用批注的columnDefinition
属性有些小小的@Column
改动,例如:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
insertable=false
该列是否可为空(并避免使用不必要的column参数)。
您可以执行以下操作:
@Column(name="price")
private double price = 0.0;
那里!您刚刚使用零作为默认值。
请注意,如果您仅从此应用程序访问数据库,这将为您提供服务。如果其他应用程序也使用该数据库,则应使用Cameron的 columnDefinition批注属性或其他方式从数据库中进行此检查。
Example
对象作为搜索原型的条件查询。设置默认值后,Hibernate示例查询将不再忽略关联的列,因为该列为空,因此以前该位置将忽略它。更好的方法是在调用Hibernate save()
或之前设置所有默认值update()
。这更好地模仿了数据库的行为,该行为在保存行时设置默认值。
null
例如设置)设置默认值。使用@PrePersist
和@PreUpdate
是更好的选择恕我直言。
columnDefinition
属性不是独立于数据库的,并且@PrePersist
在插入之前会覆盖您的设置,“默认值”是其他内容,如果未明确设置该值,则使用默认值。
另一种方法是使用javax.persistence.PrePersist
@PrePersist
void preInsert() {
if (this.createdTime == null)
this.createdTime = new Date();
}
if (createdt != null) createdt = new Date();
什么吗?现在,它将覆盖显式指定的值,这似乎使其实际上不是默认值。
if (createdt == null) createdt = new Date();
null
检查。
在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类成员来替代。
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自动装箱在这方面大有帮助。
看到我在尝试解决同一问题时偶然发现来自Google的内容时,我会提出自己准备的解决方案,以防有人发现它有用。
从我的角度来看,对于此问题,实际上只有一种解决方案-@PrePersist。如果在@PrePersist中执行此操作,则必须检查该值是否已设置。
@PrePersist
OP的用例。@Column(columnDefinition=...)
看起来不是很优雅。
@Column(columnDefinition="tinyint(1) default 1")
我刚刚测试了这个问题。它工作正常。感谢您的提示。
关于评论:
@Column(name="price")
private double price = 0.0;
当然,这并没有设置数据库中的默认列值。
您可以使用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();
}
}
}
Field[] fields = object.getClass().getDeclaredFields();
插入for()
。并添加final
到您的参数/捕获的异常中,因为您不想object
被意外修改。还要添加一个检查null
:if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }
。这样可以确保object.getClass()
调用安全并且不会触发NPE
。原因是为了避免懒惰的程序员犯错误。;-)
我使用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='...'
,然后数据库将自动从数据库中插入默认值。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和Hibernate批注都不支持默认列值的概念。要解决此限制,请在调用Hibernate之前save()
或update()
在会话上设置所有默认值。这尽可能接近(缺少Hibernate设置默认值)模仿了数据库的行为,该行为是在表中保存一行时设置默认值的。
与该替代答案所建议的在模型类中设置默认值不同,此方法还确保将Example
对象作为搜索原型的条件查询将继续像以前一样工作。当您在模型类中设置可为空的属性(具有非原始类型的属性)的默认值时,Hibernate的示例查询将不再忽略关联的列,因为以前它会为空,因此它将忽略该列。
在JPA中这是不可能的。
这是您可以对Column注解执行的操作:http : //java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
您需要insertable=false
在@Column
注释中。JPA将在插入数据库时忽略该列,并使用默认值。
看到这个链接:http : //mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html
nullable=false
将会失败SqlException
:Caused by: java.sql.SQLException: Column 'opening_times_created' cannot be null
。在这里,我忘了用设置“创建的”时间戳openingTime.setOpeningCreated(new Date())
。这是保持一致性的一种好方法,但这是发问者没有问的问题。