JPA'daki sütunlar için varsayılan değerleri ayarlama


Yanıtlar:


230

Aslında ek açıklama columnDefinitionözelliğini kullanarak biraz hack olmasına rağmen, JPA'da mümkündür @Column, örneğin:

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

3
10, Tamsayı kısmıdır ve 2, sayının ondalık kısmıdır; bu nedenle: 1234567890.12 desteklenen bir sayı olacaktır.
Nathan Feger

23
Ayrıca, bu columnDefinition'ın veritabanından bağımsız olması gerekmez ve Java'daki veri türüne otomatik olarak bağlı değildir.
Nathan Feger

47
Boş alanı olan bir varlık oluşturup devam ettirdikten sonra, içinde ayarlanmış değere sahip olmazsınız. Çözüm değil ...
Pascal Thivent

18
Hayır @NathanFeger, 10 uzunluğu ve 2 ondalık kısmıdır. Yani 1234567890.12 desteklenen bir sayı değildir, ancak 12345678.90 geçerlidir.
GPrimola

4
insertable=falseSütunun boş bırakılabilir olması gerekir (ve gereksiz sütun bağımsız değişkeninden kaçınmak için).
Eckes

314

Aşağıdakileri yapabilirsiniz:

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

Orada! Varsayılan değer olarak sıfır kullandınız.

Yalnızca bu uygulamadan veritabanına erişiyorsanız bunun size hizmet edeceğini unutmayın. Diğer uygulamalar da veritabanını kullanıyorsa, bu denetimi Cameron'ın columnDefinition ek açıklama özelliğini veya başka bir yolu kullanarak veritabanından yapmalısınız .


38
Varlıklarınızın ekleme işleminden sonra doğru değere sahip olmasını istiyorsanız, doğru yapmanın yolu budur. +1
Pascal Thivent

16
Model sınıfında null olabilecek bir özniteliğin (ilkel olmayan türde) varsayılan değerinin ayarlanması, Examplenesneyi arama için prototip olarak kullanan ölçüt sorgularını kırar . Varsayılan bir değer ayarlandıktan sonra, Hazırda Bekleme örneği sorgusu artık ilişkili olduğu sütunu yok saymayacaktır; burada daha önce boş olduğu için yok sayılır. Daha iyi bir yaklaşım, Hazırda Bekletme save()veya işlevini başlatmadan hemen önce tüm varsayılan değerleri ayarlamaktır update(). Bu, bir satırı kaydettiğinde varsayılan değerleri ayarlayan veritabanının davranışını daha iyi taklit eder.
Derek Mahar

1
@Harry: Bu, arama için bir prototip olarak örnek kullanan ölçüt sorgularını kullanma dışında varsayılan değerleri ayarlamanın doğru yoludur.
Derek Mahar

12
Bu, ilkel olmayan türler için varsayılan ayarın ayarlanmasını sağlamaz ( nullörneğin ayar ). Kullanılması @PrePersistve @PreUpdatedaha iyi bir seçenektir IMHO.
Jasper

3
Bu, sütun için varsayılan değeri belirtmenin doğru yoludur. columnDefinitionözelliği veritabanından bağımsız değildir ve @PrePersistekleme işleminden önce ayarınızı geçersiz kılar, "varsayılan değer" başka bir şeydir, değer açıkça ayarlanmadığında varsayılan değer kullanılır.
Utku Özdemir

110

başka bir yaklaşım javax.persistence.PrePersist kullanmaktır

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

1
Zaman damgalarını eklemek ve güncellemek için böyle bir yaklaşım kullandım. Çok iyi çalıştı.
Spina

10
Bu falan değil if (createdt != null) createdt = new Date();mi? Şu anda, bu gerçekten varsayılan bir değer değil gibi görünen açıkça belirtilen bir değeri geçersiz kılacaktır.
Max Nanasy

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

İstemci ve veritabanının farklı zaman dilimlerinde olması önemli midir? "TIMESTAMP DEFAULT CURRENT_TIMESTAMP" gibi bir şey kullanmanın veritabanı sunucusunda geçerli saati alacağını ve "createt = new Date ()" java kodunda geçerli saati alacağını, ancak bu iki kez aynı olmayabilir istemci farklı bir saat diliminde bir sunucuya bağlanıyor.
Sinirli

nullÇek eklendi .
Ondra Žižka

49

2017 yılında, JPA 2.1 hala sadece @Column(columnDefinition='...') sütunun gerçek SQL tanımını koyduğunuza . Bu oldukça esnek olmayan ve JPA uygulamasının bu konudaki görüşünü kısa devre yapan, tür gibi diğer yönleri de beyan etmeye zorlar.

Hazırda bekletme modu var:

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

DDL aracılığıyla ilişkili sütuna uygulanacak VARSAYILAN değerini tanımlar.

Buna iki not:

1) Standart dışı olmaktan korkmayın. JBoss geliştiricisi olarak çalıştığım için, bazı spesifikasyon süreçleri gördüm. Spesifikasyon temel olarak, verilen alandaki büyük oyuncuların önümüzdeki on yıl için destek vermeye istekli oldukları temeldir. Güvenlik için, mesajlaşma için ORM bir fark değil (JPA oldukça fazla olsa da). Bir geliştirici olarak deneyimim, karmaşık bir uygulamada, er ya da geç, yine de standart olmayan bir API'ya ihtiyacınız olacak. Ve @ColumnDefaultstandart olmayan bir çözüm kullanmanın olumsuzluklarından daha ağır bastığı bir örnektir.

2) Herkesin @PrePersist ya da kurucu üye başlatmayı nasıl dalgaladığı çok güzel. Ancak bu aynı DEĞİLDİR. Toplu SQL güncellemelerine ne dersiniz? Sütunu ayarlamayan ifadelere ne dersiniz? DEFAULTrolü vardır ve bu bir Java sınıfı üyesi başlatılarak değiştirilemez.


Wildfly 12 için hazırda bekletme ek açıklamalarının hangi sürümünü kullanabilirim, bunun belirli bir sürümünde bir hata yakalarım? Teşekkürler ind peşin!
Fernando Pie

Teşekkürler Ondra. 2019'da başka çözüm var mı?
Alan

1
@ Ne yazık ki stokta JPA yok.
jwenting

13

JPA bunu desteklemiyor ve eğer yapsaydı faydalı olurdu. ColumnDefinition kullanımı DB'ye özgüdür ve çoğu durumda kabul edilemez. null değerleri olan (genellikle eski DBUnit sınamalarını yeniden çalıştırdığınızda gerçekleşen) bir kayıt aldığınızda sınıfta varsayılan olarak ayar yapmak yeterli değildir. Ne yaptığım bu:

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 otomatik boksu bu konuda çok yardımcı olur.


Ayarlayıcıları kötüye kullanmayın! Nesne alanına bir parametre ayarlamak içindir. Başka bir şey yok! Varsayılan değerler için varsayılan yapıcıyı kullanmanız daha iyi olur.
Roland

@Roland teoride güzel, ancak uygulamanızın sahip olmak istemediği zaten boş değerler içeren mevcut bir veritabanıyla uğraşırken her zaman çalışmaz. İlk olarak, sütunu null değil ve aynı anda mantıklı bir varsayılan ayarladığınız bir veritabanı dönüşümü yapmanız ve ardından aslında geçersiz kılınan (diğer bir deyişle veritabanını bile değiştirebilir).
jwenting

#JPA ve ayarlayıcılar / alıcılar söz konusu olduğunda, uygulamanızın büyüdüğü ve büyüdüğü ve bakım kabusu haline geldiği bir gün her zaman saf ayarlayıcı / alıcı olmalıdır. En iyi tavsiye burada KISS (ben eşcinsel değilim, LOL).
Roland

10

Aynı sorunu çözmeye çalışırken Google'dan tökezlediğimi görünce, birinin yararlı bulması durumunda pişirdiğim çözümü atacağım.

Benim açımdan, bu probleme gerçekten sadece 1 çözüm var - @PrePersist. @PrePersist içinde yaparsanız, değerin önceden ayarlanmış olup olmadığını kontrol etmeniz gerekir.


4
+1 - @PrePersistOP'nin usecase'ini kesinlikle tercih ederim . @Column(columnDefinition=...)çok zarif görünmüyor.
Piotr Nowicki

@PrePersist, null değerleri olan mağazada zaten veri varsa size yardımcı olmaz. Sizin için sihirli bir veritabanı dönüştürme yapmaz.
10'da jwenting

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

Sorunu yeni test ettim. Sadece iyi çalışıyor. İpucu için teşekkürler.


Yorumlar hakkında:

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

Bu seferki değil (elbette) veritabanında varsayılan sütun değerini ayarlayın.


9
Nesne düzeyinde iyi çalışmaz (varlığınızdaki bir eklemeden sonra veritabanı varsayılan değerini alamazsınız). Java düzeyinde varsayılan.
Pascal Thivent

Çalışır (özellikle veritabanına inserttable = false belirtirseniz, ancak talihsiz bir şekilde önbelleğe alınan sürüm yenilenmez (JPA tablo ve sütunları seçtiğinde bile değil). gidiş dönüş kötü
eckes

9

Java yansıma API'sini kullanabilirsiniz:

    @PrePersist
    void preInsert() {
       PrePersistUtil.pre(this);
    }

Bu yaygındır:

    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
Bu çözümü seviyorum, ancak bir zayıflık noktası var, bu varsayılan değer ayarlanmadan önce sütunun boş olup olmadığını kontrol etmek önemli olacağını düşünüyorum.
Luis Carlos

İsterseniz, kodu kodun Field[] fields = object.getClass().getDeclaredFields();içine koymak for()da uygun olabilir. Ayrıca , yanlışlıkla değiştirilmesini finalistemediğiniz için parametrenize / yakalanan istisnalarınıza da ekleyin object. Ayrıca bir kontrol eklemek null: if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }. Bu, object.getClass()çağrının güvenli olmasını sağlar ve a'yı tetiklemez NPE. Bunun nedeni tembel programcılar tarafından yapılan hatalardan kaçınmaktır. ;-)
Roland

7

Kullanıyorum columnDefinitionve çok iyi çalışıyor

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;

6
Bu, jpa uygulama satıcınızı özel kılıyor gibi görünüyor.
Udo

Yalnızca DDL satıcısını özel yapar. Ancak büyük olasılıkla satıcıya özel DDL saldırıları var .. Ancak yukarıda belirtildiği gibi değer (inserttable = false olsa bile) DB'de görünür ancak oturumun varlık önbelleğinde görünmez (en azından hazırda bekleme).
eckes

7

Bunu sütun ek açıklamasıyla yapamazsınız. Tek yol, bir nesne oluşturulduğunda varsayılan değeri ayarlamak olduğunu düşünüyorum. Belki de varsayılan kurucu bunu yapmak için doğru yer olacaktır.


İyi fikir. Ne yazık ki, @Columnetrafında genel bir açıklama veya özellik yok . Ayrıca ayarlanan yorumları da özlüyorum (Java doctag'den alınmıştır).
Roland

5

Benim durumumda, yeni bir açıklama eklemek için hazırda bekletme çekirdeği kaynak kodunu değiştirdim @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 );

Bu sadece hazırda bekletme çözümü.


2
Açık kaynak projesine aktif olarak katkıda bulunan insanların çabalarını takdir etsem de, bu cevabı reddettim çünkü JPA herhangi bir OR / M'nin üstünde standart bir Java spesifikasyonu olduğundan, OP varsayılan değerleri belirtmek için bir JPA yolu istedi ve yama çalışmalarınız sadece Hazırda Bekletme için. Bu, mukavemet için süper parça belirtimi olmayan NHibernate olsaydı (NPA, MS EF tarafından bile desteklenmez), bu tür bir yamayı iptal ederdim. Gerçek şu ki, JPA, ORM gereklilikleriyle karşılaştırıldığında oldukça sınırlıdır (bir örnek: ikincil dizin yok). Neyse çaba için kudos
usr-local-ΕΨΗΕΛΩΝ

Bu bir bakım sorunu yaratmıyor mu? Yükseltmeler hazırda bekletildiğinde veya her yeni kurulumda bu değişiklikleri yeniden yapmak gerekir mi?
user1242321

Soru JPA ile ilgili olduğundan ve Hazırda Beklet'ten bile bahsedilmediğinden, bu soruya cevap vermiyor
Neil Stockton

5
  1. @Column(columnDefinition='...') veri eklenirken veritabanında varsayılan kısıtlamayı ayarladığınızda çalışmaz.
  2. Ek açıklamayı yapmanız insertable = falseve kaldırmanız gerekir columnDefinition='...', ardından veritabanı varsayılan değeri veritabanından otomatik olarak ekler.
  3. Örneğin, varchar cinsiyetini ayarladığınızda veritabanında varsayılan olarak erkek olur.
  4. Sadece insertable = falseHazırda Beklet / JPA eklemeniz gerekir, işe yarayacaktır.


Ve bazen değerini ayarlamak istiyorsanız iyi bir seçenek değil.
Shihe Zhang

@ShiheZhang false kullanarak değeri ayarlamama izin vermiyor mu?
fvildoso

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

Benim durumumda bu alan kullanılan LocalDateTime nedeniyle, satıcı bağımsızlığı nedeniyle önerilir


2

Ne JPA ne de Hazırda Bekletme notları varsayılan sütun değeri kavramını desteklemez. Bu sınırlamaya geçici bir çözüm olarak, bir Hazırda Bekletme modunu başlatmadan hemen önce save()veya update()oturumda tüm varsayılan değerleri ayarlayın . Bu mümkün olduğunca yakından (Hazırda Beklet varsayılan değerlerini ayarlama kısa) veritabanı bir tabloya bir satır kaydettiğinde varsayılan değerleri ayarlayan davranışını taklit eder.

Bu alternatif yanıtın önerdiği gibi model sınıfındaki varsayılan değerlerin ayarlanmasının aksine , bu yaklaşım aynı zamanda bir Examplenesneyi arama için prototip olarak kullanan ölçüt sorgularının eskisi gibi çalışmaya devam etmesini sağlar. Bir model sınıfında null olabilecek bir özniteliğin (ilkel olmayan bir türe sahip olan) varsayılan değerini ayarladığınızda, bir örnek hazırda bekletme sorgusu artık ilişkili olduğu sütunu, daha önce boş olduğu için yoksayar.


Önceki çözüm yazar ColumnDefault ("")
nikolai.serdiuk

@ nikolai.serdiuk, bu cevap yazıldıktan yıllar sonra ColumnDefault ek açıklamasının eklendiğini gösterir. 2010 yılında doğruydu, böyle bir ek açıklama yoktu (aslında ek açıklama yoktu, sadece xml yapılandırması).
jwenting

1

Bu JPA'da mümkün değildir.

Sütun ek açıklamasıyla şunları yapabilirsiniz : http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html


1
Varsayılan bir değer belirleyememek, JPA ek açıklamalarında ciddi bir eksiklik gibi görünüyor.
Derek Mahar

2
JDO ORM tanımında buna izin veriyor, bu yüzden JPA bunu
içermelidir

Kesinlikle olabilir Cameron Papa cevap olarak, columnDefinition özniteliği ile, bunu.
IntelliData

@DerekMahar JPA şartnamesindeki tek eksiklik değil. İyi bir şartname, ama mükemmel değil
jwenting

1

Bir çift kullanıyorsanız aşağıdakileri kullanabilirsiniz:

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

private Double grolsh;

Evet db'ye özgüdür.


0

Veritabanı tasarımcısında veya tablo oluştururken varsayılan değeri tanımlayabilirsiniz. Örneğin, SQL Server'da bir Tarih alanının varsayılan kasasını ( getDate()) olarak ayarlayabilirsiniz . insertable=falseSütun tanımınızda belirtildiği gibi kullanın . JPA, bu sütun ekler üzerinde belirtmez ve veritabanı sizin için değeri oluşturur.


-2

Sen gerek insertable=falsesize de @Columnek açıklama. JPA, veritabanına eklenirken bu sütunu yoksayar ve varsayılan değer kullanılır.

Bu bağlantıya bakın: http://mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html


2
O zaman u verilen değeri @ çalışma zamanı nasıl ekleyecek ??
Ashish Ratan

Evet bu doğru. nullable=falseBir başarısız olur SqlException: Caused by: java.sql.SQLException: Column 'opening_times_created' cannot be null. Burada "oluşturulan" zaman damgası ile ayarlamak unuttum openingTime.setOpeningCreated(new Date()). Tutarlı olmanın güzel bir yoludur, ancak soru soran bu değildi.
Roland
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.