Có thể đặt giá trị mặc định cho các cột trong JPA không và nếu được thực hiện bằng cách sử dụng chú thích?
Có thể đặt giá trị mặc định cho các cột trong JPA không và nếu được thực hiện bằng cách sử dụng chú thích?
Câu trả lời:
Trên thực tế có thể có trong JPA, mặc dù một chút hack sử dụng thuộc columnDefinition
tính của @Column
chú thích, ví dụ:
@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
insertable=false
nếu cột là nullable (và để tránh đối số cột không cần thiết).
Bạn có thể làm như sau:
@Column(name="price")
private double price = 0.0;
Đó! Bạn vừa sử dụng số 0 làm giá trị mặc định.
Lưu ý điều này sẽ phục vụ bạn nếu bạn chỉ truy cập cơ sở dữ liệu từ ứng dụng này. Nếu các ứng dụng khác cũng sử dụng cơ sở dữ liệu, thì bạn nên thực hiện kiểm tra này từ cơ sở dữ liệu bằng cách sử dụng thuộc tính chú thích Định nghĩa cột của Cameron hoặc một số cách khác.
Example
đối tượng làm nguyên mẫu cho tìm kiếm. Sau khi đặt giá trị mặc định, truy vấn ví dụ Hibernate sẽ không còn bỏ qua cột được liên kết mà trước đó nó sẽ bỏ qua nó vì nó là null. Cách tiếp cận tốt hơn là đặt tất cả các giá trị mặc định ngay trước khi gọi Hibernate save()
hoặc update()
. Điều này tốt hơn bắt chước hành vi của cơ sở dữ liệu đặt các giá trị mặc định khi nó lưu một hàng.
null
ví dụ: cài đặt ). Sử dụng @PrePersist
và @PreUpdate
là một lựa chọn tốt hơn imho.
columnDefinition
thuộc tính không độc lập với cơ sở dữ liệu và @PrePersist
ghi đè cài đặt của bạn trước khi chèn, "giá trị mặc định" là một thứ khác, giá trị mặc định được sử dụng khi giá trị không được đặt rõ ràng.
một cách tiếp cận khác là sử dụng javax.persistence.PrePersist
@PrePersist
void preInsert() {
if (this.createdTime == null)
this.createdTime = new Date();
}
if (createdt != null) createdt = new Date();
hay sao? Ngay bây giờ, điều này sẽ ghi đè một giá trị được chỉ định rõ ràng, dường như làm cho nó không thực sự là một mặc định.
if (createdt == null) createdt = new Date();
null
kiểm tra.
Trong năm 2017, JPA 2.1 vẫn chỉ @Column(columnDefinition='...')
có bạn đặt định nghĩa SQL theo nghĩa đen của cột. Điều này khá không linh hoạt và buộc bạn cũng phải khai báo các khía cạnh khác như kiểu, làm ngắn mạch quan điểm của việc triển khai JPA về vấn đề đó.
Hibernate mặc dù, có điều này:
@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;
Xác định giá trị DEFAULT để áp dụng cho cột được liên kết thông qua DDL.
Hai lưu ý rằng:
1) Đừng sợ đi không chuẩn. Làm việc như một nhà phát triển JBoss, tôi đã thấy khá nhiều quy trình đặc tả. Thông số kỹ thuật về cơ bản là cơ sở mà các cầu thủ lớn trong lĩnh vực nhất định sẵn sàng cam kết hỗ trợ trong thập kỷ tới hoặc lâu hơn. Điều đó đúng với bảo mật, đối với nhắn tin, ORM không có gì khác biệt (mặc dù JPA bao gồm khá nhiều). Kinh nghiệm của tôi với tư cách là một nhà phát triển là trong một ứng dụng phức tạp, sớm hay muộn bạn cũng sẽ cần một API không chuẩn. Và @ColumnDefault
là một ví dụ khi nó vượt xa các tiêu cực của việc sử dụng một giải pháp không chuẩn.
2) Thật tuyệt khi mọi người vẫy tay khởi tạo thành viên @PrePersist hoặc constructor. Nhưng điều đó KHÔNG giống nhau. Làm thế nào về cập nhật SQL số lượng lớn? Làm thế nào về các báo cáo không đặt cột? DEFAULT
có vai trò của nó và điều đó không thể thay thế bằng cách khởi tạo một thành viên lớp Java.
JPA không hỗ trợ điều đó và nó sẽ hữu ích nếu có. Sử dụng cộtDefDef là DB cụ thể và không được chấp nhận trong nhiều trường hợp. thiết lập mặc định trong lớp là không đủ khi bạn truy xuất một bản ghi có giá trị null (điều này thường xảy ra khi bạn chạy lại các bài kiểm tra DBUnit cũ). Những gì tôi làm là thế này:
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 tự động đấm bốc giúp rất nhiều trong đó.
Thấy tôi vấp phải điều này từ Google trong khi cố gắng giải quyết vấn đề tương tự, tôi sẽ đưa ra giải pháp tôi đã nấu trong trường hợp ai đó thấy nó hữu ích.
Theo quan điểm của tôi, thực sự chỉ có 1 giải pháp cho vấn đề này - @PrePersist. Nếu bạn làm điều đó trong @PrePersist, bạn phải kiểm tra xem giá trị đã được đặt chưa.
@PrePersist
trình usecase của OP. @Column(columnDefinition=...)
không có vẻ rất thanh lịch.
@Column(columnDefinition="tinyint(1) default 1")
Tôi chỉ thử nghiệm vấn đề. Nó hoạt động tốt. Cảm ơn đã gợi ý.
Về các ý kiến:
@Column(name="price")
private double price = 0.0;
Điều này không đặt giá trị cột mặc định trong cơ sở dữ liệu (tất nhiên).
bạn có thể sử dụng java phản ánh api:
@PrePersist
void preInsert() {
PrePersistUtil.pre(this);
}
Điều này là phổ biến:
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();
vào cũng for()
có thể ổn. Và cũng thêm final
vào tham số / ngoại lệ bị bắt vì bạn không muốn object
bị sửa đổi do tai nạn. Ngoài ra, thêm một kiểm tra về null
: if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }
. Điều đó đảm bảo rằng object.getClass()
an toàn để gọi và không kích hoạt a NPE
. Lý do là để tránh những sai lầm của các lập trình viên lười biếng. ;-)
Tôi sử dụng columnDefinition
và nó hoạt động rất tốt
@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date createdDate;
Bạn không thể làm điều này với chú thích cột. Tôi nghĩ cách duy nhất là đặt giá trị mặc định khi một đối tượng được tạo. Có lẽ các nhà xây dựng mặc định sẽ là nơi thích hợp để làm điều đó.
@Column
xung quanh. Và tôi cũng bỏ lỡ các bình luận được đặt (lấy từ tài liệu Java).
Trong trường hợp của tôi, tôi đã sửa đổi mã nguồn lõi ngủ đông để giới thiệu một chú thích mới @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 );
Vâng, đây là một giải pháp chỉ ngủ đông.
@Column(columnDefinition='...')
không hoạt động khi bạn đặt ràng buộc mặc định trong cơ sở dữ liệu trong khi chèn dữ liệu.insertable = false
và xóa columnDefinition='...'
khỏi chú thích, sau đó cơ sở dữ liệu sẽ tự động chèn giá trị mặc định từ cơ sở dữ liệu.insertable = false
vào Hibernate / JPA, nó sẽ hoạt động.@PrePersist
void preInsert() {
if (this.dateOfConsent == null)
this.dateOfConsent = LocalDateTime.now();
if(this.consentExpiry==null)
this.consentExpiry = this.dateOfConsent.plusMonths(3);
}
Trong trường hợp của tôi do trường là LocalDateTime tôi đã sử dụng, trường này được khuyến nghị do tính độc lập của nhà cung cấp
Cả chú thích JPA và Hibernate đều không hỗ trợ khái niệm giá trị cột mặc định. Để khắc phục hạn chế này, hãy đặt tất cả các giá trị mặc định ngay trước khi bạn gọi Hibernate save()
hoặc update()
trên phiên. Điều này càng chặt chẽ càng tốt (viết tắt là Hibernate thiết lập các giá trị mặc định) bắt chước hành vi của cơ sở dữ liệu đặt các giá trị mặc định khi nó lưu một hàng trong bảng.
Không giống như đặt các giá trị mặc định trong lớp mô hình như câu trả lời thay thế này cho thấy, cách tiếp cận này cũng đảm bảo rằng các truy vấn tiêu chí sử dụng một Example
đối tượng làm nguyên mẫu cho tìm kiếm sẽ tiếp tục hoạt động như trước. Khi bạn đặt giá trị mặc định của thuộc tính nullable (thuộc tính có kiểu không nguyên thủy) trong lớp mô hình, ví dụ truy vấn Hibernate sẽ không còn bỏ qua cột được liên kết mà trước đó nó sẽ bỏ qua vì nó là null.
Điều này là không thể trong JPA.
Đây là những gì bạn có thể làm với chú thích Cột: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
Nếu bạn đang sử dụng gấp đôi, bạn có thể sử dụng như sau:
@Column(columnDefinition="double precision default '96'")
private Double grolsh;
Vâng, đó là db cụ thể.
Bạn có thể xác định giá trị mặc định trong trình thiết kế cơ sở dữ liệu hoặc khi bạn tạo bảng. Chẳng hạn, trong SQL Server, bạn có thể đặt vault mặc định của trường Ngày thành ( getDate()
). Sử dụng insertable=false
như được đề cập trong định nghĩa cột của bạn. JPA sẽ không chỉ định cột đó khi chèn và cơ sở dữ liệu sẽ tạo ra giá trị cho bạn.
Bạn cần insertable=false
trong @Column
chú thích của bạn . JPA sẽ bỏ qua cột đó trong khi chèn vào cơ sở dữ liệu và giá trị mặc định sẽ được sử dụng.
Xem liên kết này: http://mariemjabloun.blogspot.com/2014/03/resolve-set-database-default-value-in.html
nullable=false
sẽ thất bại với SqlException
: Caused by: java.sql.SQLException: Column 'opening_times_created' cannot be null
. Ở đây tôi quên đặt dấu thời gian "đã tạo" với openingTime.setOpeningCreated(new Date())
. Đây là một cách hay để có sự nhất quán nhưng đó là điều người hỏi không hỏi.