在JPA中为列设置默认值
是否有可能为JPA中的列设置默认值,以及如何使用注释完成该操作?
实际上,在JPA中是可能的,尽pipe使用@Column
注解的columnDefinition
属性有点破解,例如:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
您可以执行以下操作:
@Column(name="price") private double price = 0.0;
那里! 您刚刚使用零作为默认值。
注意,如果你只是从这个应用程序访问数据库,这将为您服务。 如果其他应用程序也使用数据库,那么您应该使用Cameron的 columnDefinition注释属性或其他方式从数据库中进行检查。
另一种方法是使用javax.persistence.PrePersist
@PrePersist void preInsert() { if (this.createdTime == null) this.createdTime = new Date(); }
在2017年,JPA仍然只有@Column(columnDefinition='...')
,您可以将该列的字面SQL定义添加到该列中。 这是非常不灵活的,迫使你也声明types的其他方面,JPA实现的短视这个问题。
hibernate虽然,有这样的:
@Column(length = 4096, nullable = false) @org.hibernate.annotations.ColumnDefault("") private String description;
标识通过DDL应用于关联列的DEFAULT值。
hibernate4.3文档 (4.3来显示它已经在这里相当一段时间)
Hibernate手册上的数据库列的默认值
两点说明:
1)不要害怕走向不规范。 作为一名JBoss开发人员,我已经看到了一些规范stream程。 规范基本上是给定领域的大玩家愿意承诺在未来十年左右支持的基准。 对于安全性来说是这样,对于消息传递来说,ORM没有什么区别(尽pipeJPA覆盖了很多)。 我作为一个开发人员的经验是,在一个复杂的应用程序中,迟早你将需要一个非标准的API。 而@ColumnDefault
就是一个例子,它超越了标准解决scheme的负面影响。
2)每个人都如何挥舞@PrePersist或者构造函数成员初始化是很好的。 但是那不是一样的。 如何批量SQL更新? 如何不设置列的语句? DEFAULT
有它的作用,并且不能通过初始化Java类成员来替代。
JPA不支持,如果有的话,这将是有用的。 使用columnDefinition是DB特定的,在许多情况下不可接受。 当您检索具有空值的logging(通常在您重新运行旧的DBUnittesting时发生)时,在类中设置缺省值是不够的。 我做的是这样的:
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的这个问题,我只是想扔掉我制作的解决scheme,以防有人发现它有用。
从我的angular度来看,这个问题真的只有一个解决scheme – @PrePersist。 如果你在@PrePersist中做,你必须检查是否已经设置了值。
我使用columnDefinition
,它工作得很好
@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP") private Date createdDate;
你不能使用列注释来做到这一点。 我认为唯一的方法是在创build对象时设置默认值。 也许默认的构造函数将是正确的地方。
@Column(columnDefinition="tinyint(1) default 1")
我刚刚testing了这个问题。 它工作得很好。 感谢提示。
关于评论:
@Column(name="price") private double price = 0.0;
这个不会在数据库中设置默认的列值(当然)。
就我而言,我修改了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 );
那么,这是一个hibernate的解决scheme。
你可以使用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(); } } }
JPA和Hibernate注释都不支持默认列值的概念。 作为解决此限制的一个解决方法,在会话上调用Hibernate save()
或update()
之前设置所有默认值。 尽可能地紧密(Hibernate设置缺省值)模仿数据库的行为,当它在表中保存一行时,它设置默认值。
不同于在模型类中设置默认值,正如这个备选答案所暗示的,这种方法也确保了使用Example
对象作为search原型的条件查询将继续像以前一样工作。 当你在一个模型类中设置一个可空属性的默认值(一个非基元types)时,一个Hibernate查询的例子将不再忽略之前忽略它的关联列,因为它是空的。
这在JPA中是不可能的。
以下是Column注释的用法: http : //java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
如果您使用双精度型,则可以使用以下方法:
@Column(columnDefinition="double precision default '96'") private Double grolsh;
是的,它是特定于数据库。
您可以在数据库devise器中或在创build表格时定义默认值。 例如在SQL Server中,可以将Date字段的默认Vault设置为( getDate()
)。 如列定义中所述,使用insertable=false
。 JPA不会在插入时指定该列,数据库将为您生成值。
- 在插入数据时,在数据库中设置默认约束时,
@Column(columnDefinition='...')
不起作用。 - 您需要使
insertable = false
并从注释中移除columnDefinition='...'
,然后数据库会自动从数据库中插入默认值。 - 例如,当你设置varchar性别在数据库中是默认的男性。
- 你只需要在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我使用这个,build议由于供应商的独立性
您需要在@Column
注释中insertable=false
。 JPA将插入数据库时忽略该列,并使用默认值。
看到这个链接: http : //mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html