เป็นไปได้หรือไม่ที่จะตั้งค่าเริ่มต้นสำหรับคอลัมน์ใน JPA และถ้าเป็นเช่นนั้นทำได้อย่างไรโดยใช้คำอธิบายประกอบ
เป็นไปได้หรือไม่ที่จะตั้งค่าเริ่มต้นสำหรับคอลัมน์ใน JPA และถ้าเป็นเช่นนั้นทำได้อย่างไรโดยใช้คำอธิบายประกอบ
คำตอบ:
จริงๆแล้วมันเป็นไปได้ใน JPA แม้ว่าจะมีการแฮ็คเล็กน้อยโดยใช้columnDefinition
คุณสมบัติของ@Column
คำอธิบายประกอบตัวอย่างเช่น:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
insertable=false
ถ้าคอลัมน์เป็นโมฆะ (และเพื่อหลีกเลี่ยงการโต้แย้งคอลัมน์ที่ไม่จำเป็น)
คุณสามารถทำสิ่งต่อไปนี้:
@Column(name="price")
private double price = 0.0;
มี! คุณเพิ่งใช้ศูนย์เป็นค่าเริ่มต้น
หมายเหตุสิ่งนี้จะให้บริการคุณหากคุณเข้าถึงฐานข้อมูลจากแอปพลิเคชันนี้เท่านั้น หากแอปพลิเคชันอื่นใช้ฐานข้อมูลด้วยคุณควรทำการตรวจสอบนี้จากฐานข้อมูลโดยใช้แอตทริบิวต์การเพิ่มความคิดเห็น columnDefinition ของ Cameron หรือวิธีอื่น
Example
วัตถุเป็นต้นแบบสำหรับการค้นหา หลังจากตั้งค่าเริ่มต้นแล้วแบบสอบถามตัวอย่างไฮเบอร์เนตจะไม่เพิกเฉยคอลัมน์ที่เกี่ยวข้องอีกต่อไปซึ่งก่อนหน้านี้จะเพิกเฉยต่อเนื่องจากคอลัมน์นั้นว่างเปล่า วิธีที่ดีคือการตั้งค่าเริ่มต้นทั้งหมดเป็นเพียงแค่ก่อนที่จะตื่นตาตื่นใจ Hibernate หรือsave()
update()
สิ่งนี้จะเลียนแบบพฤติกรรมของฐานข้อมูลที่ดีกว่าซึ่งจะเป็นการตั้งค่าเริ่มต้นเมื่อบันทึกแถว
null
ตัวอย่างเช่นการตั้งค่า) การใช้@PrePersist
และ@PreUpdate
เป็นตัวเลือกที่ดีกว่า
columnDefinition
property ไม่เป็นอิสระจากฐานข้อมูลและ@PrePersist
แทนที่การตั้งค่าของคุณก่อนที่จะแทรก "default value" เป็นอย่างอื่นค่าเริ่มต้นจะถูกใช้เมื่อค่าไม่ได้ตั้งค่าไว้อย่างชัดเจน
วิธีอื่นคือการใช้ 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
ตรวจสอบ
ในปี 2560 JPA 2.1 ยังคงมีเพียง@Column(columnDefinition='...')
คุณใส่นิยาม SQL ตามตัวอักษรของคอลัมน์ ซึ่งค่อนข้างไม่ยืดหยุ่นและบังคับให้คุณประกาศแง่มุมอื่น ๆ เช่นประเภททำให้เกิดมุมมองการใช้งาน JPA สั้นในเรื่องนั้น
แม้ว่าไฮเบอร์เนตมี:
@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;
ระบุค่าเริ่มต้นเพื่อนำไปใช้กับคอลัมน์ที่เกี่ยวข้องผ่าน DDL
หมายเหตุสองข้อต่อไปนี้:
1) อย่ากลัวที่จะไม่ได้มาตรฐาน ทำงานเป็นนักพัฒนา JBoss ฉันเห็นกระบวนการสเปคค่อนข้างมาก สเปคนั้นเป็นพื้นฐานที่ผู้เล่นรายใหญ่ในสาขานั้นยินดีที่จะให้การสนับสนุนในทศวรรษหน้าหรือมากกว่านั้น มันเป็นเรื่องจริงเพื่อความปลอดภัยสำหรับการส่งข้อความ ORM ไม่แตกต่างกัน (แม้ว่า JPA จะครอบคลุมค่อนข้างมาก) ประสบการณ์ของฉันในฐานะนักพัฒนาคือในแอปพลิเคชันที่ซับซ้อนไม่ช้าก็เร็วคุณจะต้องใช้ API ที่ไม่ได้มาตรฐาน และ@ColumnDefault
เป็นตัวอย่างเมื่อเปรียบเทียบกับข้อเสียของการใช้โซลูชันที่ไม่ได้มาตรฐาน
2) มันเป็นเรื่องดีที่ทุกคนส่งเสียง @PrePersist หรือการกำหนดค่าเริ่มต้นของสมาชิกคอนสตรัค แต่นั่นไม่เหมือนกัน ปรับปรุง SQL จำนวนมากได้อย่างไร วิธีการเกี่ยวกับงบที่ไม่ได้ตั้งค่าคอลัมน์? DEFAULT
มีบทบาทและไม่สามารถทดแทนได้ด้วยการเริ่มต้นสมาชิกคลาส Java
JPA ไม่สนับสนุนสิ่งนั้นและมันจะมีประโยชน์ถ้ามันทำ การใช้ columnDefinition เป็นเฉพาะ DB และไม่สามารถยอมรับได้ในหลายกรณี การตั้งค่าเริ่มต้นในชั้นเรียนไม่เพียงพอเมื่อคุณเรียกข้อมูลระเบียนที่มีค่า Null (ซึ่งโดยทั่วไปจะเกิดขึ้นเมื่อคุณเรียกใช้การทดสอบ 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;
}
}
ชวาอัตโนมัติชกมวยช่วยมากในที่
เมื่อฉันเห็นสิ่งนี้จาก Google ในขณะที่พยายามแก้ไขปัญหาเดียวกันฉันจะทิ้งวิธีแก้ปัญหาที่ฉันทำไว้ในกรณีที่มีคนพบว่ามีประโยชน์
จากมุมมองของฉันมีเพียง 1 ทางออกสำหรับปัญหานี้ - @PrePersist หากคุณทำใน @PrePersist คุณต้องตรวจสอบว่ามีการตั้งค่าไว้แล้ว
@PrePersist
Usecase ของ OP แน่นอน @Column(columnDefinition=...)
ดูเหมือนจะไม่หรูหรามาก
@Column(columnDefinition="tinyint(1) default 1")
ฉันเพิ่งทดสอบปัญหา มันใช้งานได้ดี ขอบคุณสำหรับคำใบ้
เกี่ยวกับความคิดเห็น:
@Column(name="price")
private double price = 0.0;
อันนี้ไม่ได้ตั้งค่าคอลัมน์เริ่มต้นในฐานข้อมูล (แน่นอน)
คุณสามารถใช้ java reflect 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;
คุณทำสิ่งนี้ไม่ได้ด้วยคำอธิบายประกอบคอลัมน์ ฉันคิดว่าวิธีเดียวคือการตั้งค่าเริ่มต้นเมื่อวัตถุถูกสร้างขึ้น บางทีคอนสตรัคเตอร์เริ่มต้นอาจเป็นที่ที่เหมาะสมในการทำเช่นนั้น
@Column
ประมาณ และฉันก็พลาดความเห็นที่ตั้งไว้ (นำมาจากหลักคำสอนของ Java)
ในกรณีของฉันฉันปรับเปลี่ยนซอร์สโค้ด 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()
ในเซสชัน สิ่งนี้ใกล้เคียงที่สุด (สั้น ๆ ของไฮเบอร์เนตตั้งค่าเริ่มต้น) เลียนแบบพฤติกรรมของฐานข้อมูลที่ตั้งค่าเริ่มต้นเมื่อมันบันทึกแถวในตาราง
แตกต่างจากการตั้งค่าเริ่มต้นในคลาสรุ่นตามที่คำตอบอื่นแนะนำวิธีนี้ยังช่วยให้มั่นใจได้ว่าแบบสอบถามแบบใช้เกณฑ์ที่ใช้Example
วัตถุเป็นแบบตัวอย่างสำหรับการค้นหาจะทำงานต่อไปเหมือนเดิม เมื่อคุณตั้งค่าเริ่มต้นของแอตทริบิวต์เป็นโมฆะ (หนึ่งที่มีชนิดที่ไม่ใช่แบบดั้งเดิม) ในรูปแบบคลาสแบบสอบถามไฮเบอร์เนตแบบสอบถามโดยตัวอย่างจะไม่สนใจคอลัมน์ที่เกี่ยวข้องที่ก่อนหน้านี้มันจะไม่สนใจเพราะมันเป็นโมฆะ
ไม่สามารถทำได้ใน JPA
นี่คือสิ่งที่คุณสามารถทำได้กับหมายเหตุประกอบคอลัมน์: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
หากคุณใช้คู่คุณสามารถใช้สิ่งต่อไปนี้:
@Column(columnDefinition="double precision default '96'")
private Double grolsh;
ใช่มันเป็น db เฉพาะ
คุณสามารถกำหนดค่าเริ่มต้นในตัวออกแบบฐานข้อมูลหรือเมื่อคุณสร้างตาราง เช่นใน SQL Server คุณสามารถตั้งค่า vault เริ่มต้นของเขตข้อมูล Date เป็น ( getDate()
) ใช้insertable=false
ตามที่ระบุไว้ในคำจำกัดความคอลัมน์ของคุณ JPA จะไม่ระบุคอลัมน์นั้นในส่วนแทรกและฐานข้อมูลจะสร้างมูลค่าให้คุณ
คุณจำเป็นต้อง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())
ประทับเวลาด้วย นี่เป็นวิธีที่ดีในการมีความมั่นคง แต่นั่นคือสิ่งที่ผู้ถามไม่ได้ถาม