JPA에서 열의 기본값 설정


답변:


230

실제로 JPA에서 가능하지만 주석 의 columnDefinition속성을 사용하는 약간의 해킹 은 다음과 @Column같습니다.

@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")

3
10은 정수 부분이고 2는 숫자의 소수 부분이므로 1234567890.12는 지원되는 숫자입니다.
Nathan Feger

23
또한이 columnDefinition은 데이터베이스 독립적 일 필요는 없으며 Java의 데이터 유형과 자동으로 연결되지는 않습니다.
Nathan Feger

47
널 필드가있는 엔티티를 작성하고 유지 한 후에는 값을 설정하지 않아도됩니다. 해결책이 아닙니다 ...
Pascal Thivent

18
@NathanFeger는 없습니다. 10은 길이이고 2는 소수입니다. 따라서 1234567890.12는 지원되는 숫자가 아니지만 12345678.90은 유효합니다.
GPrimola

4
당신은 필요 insertable=false컬럼이 널 (NULL) 인 경우 (그리고 불필요한 열 인수를 피하기 위해).
eckes

314

다음을 수행 할 수 있습니다.

@Column(name="price")
private double price = 0.0;

그곳에! 방금 기본값으로 0을 사용했습니다.

이 응용 프로그램에서만 데이터베이스에 액세스하는 경우이 기능이 제공됩니다. 다른 응용 프로그램도 데이터베이스를 사용하는 경우 Cameron의 columnDefinition 주석 속성 또는 다른 방법을 사용하여 데이터베이스에서이 검사를 수행해야합니다 .


38
삽입 후 엔터티가 올바른 값을 갖도록하려면 올바른 방법입니다. +1
파스칼 티 벤트

16
모델 클래스에서 널 입력 가능 속성 (기본이 아닌 유형을 갖는 속성)의 기본값을 설정하면 Example오브젝트를 검색의 프로토 타입으로 사용하는 기준 조회가 중단됩니다 . 기본값을 설정 한 후, Hibernate 예제 쿼리는 이전에 널 (null)이기 때문에이를 무시했던 연관된 열을 더 이상 무시하지 않습니다. 더 나은 방법은 하이버 네이트 호출하기 전에 모든 기본 값을 설정하는 것입니다 save()또는 update(). 이것은 행을 저장할 때 기본값을 설정하는 데이터베이스의 동작을 더 잘 모방합니다.
Derek Mahar

1
@Harry : 검색의 프로토 타입으로 예제를 사용하는 기준 쿼리를 사용하는 경우를 제외하고는 기본값을 설정하는 올바른 방법입니다.
Derek Mahar

12
이것은 기본이 아닌 유형 ( null예 : 설정)에 대해 기본값이 설정되도록 보장하지 않습니다 . 사용 @PrePersist하고 @PreUpdate더 나은 옵션 imho입니다.
재스퍼

3
이것이 열의 기본값을 지정하는 올바른 방법입니다. columnDefinition속성은 데이터베이스와 무관하며 @PrePersist삽입 전에 설정을 재정의합니다. "기본값"은 다른 값이며, 값이 명시 적으로 설정되지 않은 경우 기본값이 사용됩니다.
Utku Özdemir 10

110

또 다른 접근법은 javax.persistence.PrePersist를 사용하는 것입니다.

@PrePersist
void preInsert() {
   if (this.createdTime == null)
       this.createdTime = new Date();
}

1
타임 스탬프를 추가하고 업데이트하기 위해 이와 같은 접근 방식을 사용했습니다. 매우 잘 작동했습니다.
Spina

10
그렇지 if (createdt != null) createdt = new Date();않습니까? 현재 명시 적으로 지정된 값을 무시하여 실제로 기본값이 아닌 것으로 보입니다.
Max Nanasy

16
@MaxNanasyif (createdt == null) createdt = new Date();
Shane

클라이언트와 데이터베이스가 다른 시간대에있는 경우 문제가됩니까? "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"와 같은 것을 사용하면 데이터베이스 서버에서 현재 시간을 얻고 "createdt = new Date ()"는 Java 코드에서 현재 시간을 얻지 만 두 번의 경우 동일하지 않을 수 있습니다. 클라이언트가 다른 시간대의 서버에 연결하고 있습니다.
FrustratedWithFormsDesigner

null수표를 추가했습니다 .
Ondra Žižka

49

2017 년에도 JPA 2.1 @Column(columnDefinition='...')에는 열의 리터럴 SQL 정의 만 넣을 수 있습니다. 이것은 상당히 융통성이 없으며 유형과 같은 다른 측면을 선언하여 해당 문제에 대한 JPA 구현의 견해를 단축시켜야합니다.

그러나 최대 절전 모드는 다음과 같습니다.

@Column(length = 4096, nullable = false)
@org.hibernate.annotations.ColumnDefault("")
private String description;

DDL을 통해 연관된 열에 적용 할 DEFAULT 값을 식별합니다.

그것에 대한 두 가지 메모 :

1) 비표준으로가는 것을 두려워하지 마십시오. JBoss 개발자로 일하면서 꽤 많은 사양 프로세스를 보았습니다. 사양은 기본적으로 주어진 분야의 대기업들이 향후 10 년 정도 동안 지원하기로 약속 한 기준입니다. 보안, 메시징, ORM은 차이가 없습니다 (JPA는 상당히 많은 것을 다루지 만). 개발자로서의 경험은 복잡한 응용 프로그램에서 조만간 비표준 API가 필요하다는 것입니다. 과@ColumnDefault 비표준 솔루션을 사용하는 것의 단점을 능가하는 예입니다.

2) 모두가 @PrePersist 또는 생성자 멤버 초기화를 흔들어 놓는 것이 좋습니다. 그러나 그것은 동일하지 않습니다. 대량 SQL 업데이트는 어떻습니까? 열을 설정하지 않은 문장은 어떻습니까? DEFAULT역할이 있으며 Java 클래스 멤버를 초기화하여 대체 할 수는 없습니다.


Wildfly 12에 어떤 버전의 최대 절전 모드 주석을 사용할 수 있습니까?이 버전의 특정 버전에서 오류가 발생합니까? 미리 감사드립니다!
페르난도 파이

감사합니다 Ondra. 2019 년에 더 많은 솔루션이 있습니까?
Alan

1
@Alan JPA 재고가 없습니다. 슬프게도.
jwenting

13

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;
    }
}

Java 자동 복싱은 그 점에서 많은 도움이됩니다.


세터를 남용하지 마십시오! 개체 필드에서 매개 변수를 설정하기위한 것입니다. 더 이상! 기본값에 기본 생성자를 사용하는 것이 좋습니다.
Roland

@Roland는 이론적으로는 좋지만 응용 프로그램에서 원하지 않는 null 값이 이미 포함 된 기존 데이터베이스를 처리 할 때 항상 작동하지는 않습니다. 먼저 열을 null로 만들지 않고 합리적인 기본값을 설정 한 다음 데이터베이스를 실제로 변환 할 수있는 데이터베이스 변환을 수행 한 다음 실제로 null을 허용한다고 가정하는 다른 응용 프로그램의 백래시를 처리해야합니다 (즉, 데이터베이스를 수정할 수도 있습니다).
jwenting September

#JPA 및 세터 / 게터에 관해서는, 언젠가는 응용 프로그램이 성장하고 성장하여 유지 보수의 악몽이되는 언젠가는 순수한 세터 / 게터 여야합니다. 최고의 조언은 KISS입니다 (나는 게이가 아닙니다, LOL).
Roland

10

내가 똑같은 문제를 해결하려고 노력하면서 Google에서 이것을 우연히 보았을 때 누군가가 유용하다고 생각하는 경우 요리 한 솔루션을 던질 것입니다.

내 관점에서 볼 때,이 문제에 대한 해결책은 @PrePersist에 불과합니다. @PrePersist에서 그렇게하면 값이 이미 설정되어 있는지 확인해야합니다.


4
+1- @PrePersistOP의 사용 사례를 확실히 선택했습니다 . @Column(columnDefinition=...)매우 우아하지 않은 것 같습니다.
Piotr Nowicki

상점에 null 값을 가진 데이터가 이미 있으면 @PrePersist가 도움이되지 않습니다. 마술처럼 데이터베이스 변환을 수행하지는 않습니다.
jwenting

10
@Column(columnDefinition="tinyint(1) default 1")

방금 문제를 테스트했습니다. 잘 작동합니다. 힌트 주셔서 감사합니다.


의견에 관하여 :

@Column(name="price") 
private double price = 0.0;

이것은 데이터베이스의 기본 열 값을 설정 하지 않습니다 (물론).


9
객체 수준에서 제대로 작동 하지 않습니다 (엔티티에 삽입 한 후 데이터베이스 기본값을 얻지 못함). Java 레벨에서 기본값입니다.
파스칼 티 벤트

(특히 데이터베이스에 insertable = false를 지정하면 불행히도 캐시 된 버전은 새로 고쳐지지 않습니다 (JPA가 테이블과 열을 선택할 때조차도)가 아닙니다) 왕복이 나쁘다
eckes 1

9

당신은 자바 반영 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();
            }
        }
    }

2
나는이 솔루션을 좋아하지만 한 가지 약점을 가지고 있는데, 기본값을 설정하기 전에 열이 널 입력 가능 여부를 확인하는 것이 중요하다고 생각합니다.
Luis Carlos

원하는 경우에서 코드를 넣어 Field[] fields = object.getClass().getDeclaredFields();(가)에 for()도 괜찮을 수 있습니다. 또한 실수로 수정 final하고 싶지 않기 때문에 매개 변수 / 포착 예외에 추가 하십시오 object. 또한에 체크를 추가 null: if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }. 따라서 object.getClass()호출하기에 안전하고을 트리거하지 않습니다 NPE. 그 이유는 게으른 프로그래머가 실수를 피하는 것입니다. ;-)
Roland

7

나는 사용 columnDefinition하고 아주 잘 작동합니다

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;

6
이것은 jpa 구현 공급 업체를 특정하게 만드는 것처럼 보입니다.
Udo 개최

DDL 공급 업체 만 지정합니다. 그러나 공급 업체별 DDL 해킹 가능성이 가장 높습니다. 그러나 위에서 언급 한 것처럼 (insertable = false 인 경우에도) 값은 DB에는 표시되지만 세션의 엔터티 캐시에는 표시되지 않습니다 (최소한 최대 절전 모드는 아님).
eckes

7

열 주석으로는이 작업을 수행 할 수 없습니다. 유일한 방법은 객체가 생성 될 때 기본값을 설정하는 것입니다. 아마도 기본 생성자가 적절한 장소 일 것입니다.


좋은 생각. 슬프게도 @Column주변에 대한 일반적인 주석이나 속성이 없습니다 . 또한 주석 설정이 누락되었습니다 (Java doctag에서 가져옴).
Roland

5

필자의 경우 새로운 주석을 도입하기 위해 최대 절전 모드 소스 코드를 수정했습니다. @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 );

글쎄, 이것은 최대 절전 모드 전용 솔루션입니다.


2
오픈 소스 프로젝트에 적극적으로 기여하는 사람들의 노력에 감사하지만 JPA는 모든 OR / M에 대한 표준 Java 사양이기 때문에이 답변을 하향 조정했습니다. OP는 기본값을 지정하고 패치 작업을 지정하는 JPA 방법을 요구했습니다. 최대 절전 모드에만 해당됩니다. 이것이 지속성에 대한 슈퍼 파트 사양 이없는 NHibernate라면 (NPA는 MS EF에서도 지원되지 않습니다), 나는 그런 종류의 패치를 포기했습니다 . 진실은 JPA가 ORM의 요구 사항에 비해 상당히 제한되어 있다는 것입니다 (예 : 보조 인덱스 없음). 노력에 어쨌든의 명성
USR-로컬 ΕΨΗΕΛΩΝ

유지 관리 문제가 발생하지 않습니까? 최대 절전 모드 업그레이드 또는 새로 설치할 때마다 이러한 변경 사항을 다시 실행해야합니까?
user1242321

질문은 JPA에 관한 것이며 Hibernate는 언급되지 않았기 때문에 이것은 질문에 대답하지 않습니다
Neil Stockton

5
  1. @Column(columnDefinition='...') 데이터를 삽입하는 동안 데이터베이스에서 기본 제약 조건을 설정하면 작동하지 않습니다.
  2. 당신은 만들고 insertable = false제거 해야합니다columnDefinition='...'주석에서 하면 데이터베이스가 데이터베이스에서 기본값을 자동으로 삽입합니다.
  3. 예를 들어 varchar 성별을 설정하면 데이터베이스에서 기본적으로 male입니다.
  4. insertable = falseHibernate / JPA 를 추가 하면됩니다.


때로는 값을 설정하려는 경우 좋은 옵션이 아닙니다.
Shihe Zhang

@ShiheZhang을 사용하면 값을 설정할 수 없습니까?
fvildoso

3
@PrePersist
void preInsert() {
    if (this.dateOfConsent == null)
        this.dateOfConsent = LocalDateTime.now();
    if(this.consentExpiry==null)
        this.consentExpiry = this.dateOfConsent.plusMonths(3);
}

필자의 경우 LocalDateTime 필드로 인해이를 사용했기 때문에 공급 업체의 독립성으로 인해 권장됩니다


2

JPA 또는 최대 절전 모드 주석은 기본 열 값의 개념을 지원하지 않습니다. 이 제한에 대한 임시 해결책으로, 최대 절전 모드를 호출하기 직전 save()또는 update()세션에서 모든 기본값을 설정하십시오 . 이것은 가능한 한 밀접하게 (기본 설정을 최대 절전 모드로 설정하지 않음) 행을 테이블에 저장할 때 기본값을 설정하는 데이터베이스의 동작을 모방합니다.

대안 답변에서 제안하는 것처럼 모델 클래스에서 기본값을 설정하는 것과 달리이 방법을 사용 Example하면 검색 프로토 타입으로 객체 를 사용하는 기준 쿼리가 이전과 같이 계속 작동합니다. 모델 클래스에서 nullable 속성 (기본이 아닌 유형을 가진 속성)의 기본값을 설정하면 Hibernate query-by-query는 이전에 null이기 때문에 무시했던 관련 열을 더 이상 무시하지 않습니다.


이전 솔루션 작성자
menthioned

@ nikolai.serdiuk이 답변이 작성된 후 몇 년 후에 ColumnDefault 주석이 추가되었습니다. 2010 년에는 정확했지만 그러한 주석은 없었습니다 (사실은 주석이 없었고 xml 구성 만있었습니다).
jwenting September

1

JPA에서는 불가능합니다.

열 주석으로 수행 할 수있는 작업은 다음과 같습니다 . http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html


1
기본값을 지정할 수 없으면 JPA 주석의 심각한 단점으로 보입니다.
Derek Mahar 2016 년

2
JDO는 ORM 정의에서 허용하므로 JPA는 이것을 포함해야합니다 ... 하루
user383680

Cameron Pope이 대답 한 것처럼 columnDefinition 속성을 사용하여 확실히 할 수 있습니다 .
IntelliData

@DerekMahar JPA 사양의 유일한 부족은 아닙니다. 좋은 사양이지만 완벽하지는 않습니다.
jwenting

1

이중을 사용하는 경우 다음을 사용할 수 있습니다.

@Column(columnDefinition="double precision default '96'")

private Double grolsh;

예, 특정 DB입니다.


0

데이터베이스 디자이너에서 또는 테이블을 작성할 때 기본값을 정의 할 수 있습니다. 예를 들어 SQL Server에서 날짜 필드의 기본 볼트를 ( getDate())로 설정할 수 있습니다 . insertable=false열 정의에 언급 된대로 사용하십시오 . JPA는 삽입시 열을 지정하지 않으며 데이터베이스가 값을 생성합니다.


-2

당신은 필요 insertable=false당신에@Column 주석에 . JPA는 데이터베이스에 삽입하는 동안 해당 열을 무시하고 기본값이 사용됩니다.

이 링크를 참조하십시오 : http://mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html


2
그런 다음 u가 주어진 값 @runtime을 삽입하는 방법 ??
Ashish Ratan

응 그것은 사실이야. :로 nullable=false실패합니다 . 여기에서로 "생성 된"타임 스탬프를 설정하는 것을 잊었습니다 . 이것은 일관성을 유지하는 좋은 방법이지만 질문자가 묻지 않은 것입니다. SqlExceptionCaused by: java.sql.SQLException: Column 'opening_times_created' cannot be nullopeningTime.setOpeningCreated(new Date())
Roland
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.