Чи можна встановити значення за замовчуванням для стовпців у 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;
Там! Ви лише використовували нуль як значення за замовчуванням.
Зверніть увагу, це послужить вам, якщо ви лише отримуєте доступ до бази даних з цієї програми. Якщо інші програми також використовують базу даних, вам слід зробити цю перевірку з бази даних, використовуючи атрибут анотації стовпця CameD ''s columnDefinition або іншим способом.
Example
об'єкт як прототип для пошуку. Після встановлення значення за замовчуванням, запит зі сплячого режиму більше не буде ігнорувати пов’язаний стовпець, де раніше він ігнорував би його, оскільки він був недійсним. Кращим підходом є встановлення всіх значень за замовчуванням безпосередньо перед тим, як викликати сплячку save()
або update()
. Це краще імітує поведінку бази даних, яка встановлює значення за замовчуванням, коли вона зберігає рядок.
null
наприклад, встановлення). Використання @PrePersist
і @PreUpdate
є кращим варіантом imho.
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 стовпця. Що досить негнучко і змушує вас також заявляти про інші аспекти, такі як тип, коротке замикання на думку щодо впровадження програми СВ.
Однак у сплячому режимі є таке:
@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;
Визначає значення DEFAULT, яке застосовується до асоційованого стовпця через DDL.
Дві примітки до цього:
1) Не бійтеся йти нестандартно. Працюючи розробником JBoss, я бачив досить певні процеси специфікації. В основному специфікація є базовою лінією, що великі гравці в даному полі готові взяти на себе підтримку протягом наступного десятиліття. Це стосується безпеки, для обміну повідомленнями, ORM - це не різниця (хоча JPA охоплює досить багато). Мій досвід розробника полягає в тому, що у складному додатку рано чи пізно вам знадобиться нестандартний API. І @ColumnDefault
це приклад, коли він переважає негативи використання нестандартного рішення.
2) Приємно, як всі махають ініціалізацією @PrePersist або члена конструктора. Але це НЕ те саме. Як щодо масових оновлень SQL? Як щодо тверджень, які не встановлюють стовпчик? DEFAULT
має свою роль, і це неможливо замінити ініціалізацією члена класу Java.
JPA це не підтримує, і було б корисно, якби це було. Використання колонкиDefinition є специфічною для БД і не прийнятна у багатьох випадках. встановлення за замовчуванням у класі недостатньо, коли ви отримуєте запис із нульовими значеннями (що зазвичай відбувається при повторному запуску старих тестів 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, намагаючись вирішити ту саму проблему, я просто кину в рішення, яке я приготував, якщо хтось вважає це корисним.
З моєї точки зору, існує справді лише 1 рішення цієї проблеми - @PrePersist. Якщо ви робите це в @PrePersist, ви повинні перевірити, чи значення вже встановлено.
@PrePersist
себе корпус ОП. @Column(columnDefinition=...)
не здається дуже елегантним.
@Column(columnDefinition="tinyint(1) default 1")
Я щойно перевірив це питання. Це працює просто чудово. Дякую за підказку.
Про коментарі:
@Column(name="price")
private double price = 0.0;
Цей параметр не встановлює значення бази стовпців за замовчуванням у базі даних (звичайно).
ви можете використовувати java refle 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();
Into The 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 doctag).
У моєму випадку я змінив вихідний код на сплячий ядро, щоб ввести нову анотацію @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
в сплячку / 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 не підтримують поняття значення стовпця за замовчуванням. Для вирішення цього обмеження встановіть усі значення за замовчуванням безпосередньо перед тим, як викликати сплячку save()
або update()
на сеансі. Це максимально наближено (за винятком режиму Hibernate, який встановлює значення за замовчуванням) імітує поведінку бази даних, яка встановлює значення за замовчуванням, коли вона зберігає рядок у таблиці.
На відміну від встановлення значень за замовчуванням у модельному класі, як підказує цей альтернативний варіант відповіді , цей підхід також забезпечує запити критеріїв, які використовуютьExample
об’єкт як прототип для пошуку, продовжуватимуть працювати, як і раніше. Коли ви встановите за замовчуванням атрибут nullable (той, який має непомітивний тип) у модельному класі, Hibernate запит за прикладом більше не буде ігнорувати пов'язаний стовпець, де раніше він ігнорував би його, оскільки він був нульовим.
Це неможливо в JPA.
Ось що можна зробити з анотацією стовпця: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html
Ви можете визначити значення за замовчуванням у дизайнері бази даних або під час створення таблиці. Наприклад, у SQL Server ви можете встановити для знамення знаходження для дату поля Date ( getDate()
). Використовуйте, insertable=false
як зазначено у визначенні стовпця. JPA не вказуватиме цей стовпчик на вставках, і база даних генеруватиме значення для вас.
Вам потрібно insertable=false
в собі@Column
анотацію. JPA тоді ігнорує цей стовпець під час вставки в базу даних і буде використано значення за замовчуванням.
Перейдіть за цим посиланням: http://mariemjabloun.blogspot.com/2014/03/ резо-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())
. Це приємний спосіб послідовності, але це не запитував запитуючий.