296

Is it possible to set a default value for columns in JPA, and if, how is it done using annotations?

homaxto
  • 5,428
  • 8
  • 37
  • 53
  • is there in the JPA sepcification where can i put default value for a column as the value for another column , because i want it to be hidden – shareef Aug 08 '16 at 08:04
  • 4
    Related: Hibernate has [@org.hibernate.annotations.ColumnDefault("foo")](https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/annotations/ColumnDefault.html) – Ondra Žižka Feb 10 '17 at 03:26
  • 2
    If you are using `columnDefinition` or `@ColumnDefault`, it is advisable to stick to standard types. https://dba.stackexchange.com/questions/53317/databases-are-there-universal-datatypes. – Oliver Jan 21 '21 at 22:14

20 Answers20

361

You can do the following:

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

There! You've just used zero as the default value.

Note this will serve you if you're only accessing the database from this application. If other applications also use the database, then you should make this check from the database using Cameron's columnDefinition annotation attribute, or some other way.

Community
  • 1
  • 1
Pablo Venturino
  • 5,208
  • 5
  • 33
  • 41
  • 41
    This is the right way to do it if you want your entities to have the correct value after insert. +1 – Pascal Thivent Jul 12 '10 at 10:15
  • 17
    Setting the default value of a nullable attribute (one that has a non-primitive type) in the model class will break criteria queries that use an `Example` object as a prototype for the search. After setting a default value, a Hibernate example query will no longer ignore the associated column where previously it would ignore it because it was null. A better approach is to set all default values just before invoking a Hibernate `save()` or `update()`. This better mimics the behaviour of the database which sets default values when it saves a row. – Derek Mahar Aug 03 '10 at 19:49
  • 2
    @Harry: This is the correct way to set default values except when you are using criteria queries that use an example as a prototype for the search. – Derek Mahar Aug 27 '10 at 22:09
  • 12
    This does not ensure that default is set for non primitive types (setting `null` for example). Using `@PrePersist` and `@PreUpdate` is a better option imho. – Jasper Oct 05 '11 at 09:18
  • 1
    I'd use default values during initialization for simple cases / simple types. In more complicated cases (i.e. when the default value is somehow calculated during runtime) it'd use `@PrePersist`. Nevertheless, both ways are fine IMO. – Piotr Nowicki Jun 08 '12 at 12:01
  • 3
    This is the right way to specify default value for column. `columnDefinition` property is not database-independent and `@PrePersist` overrides your setting before insert, "default value" is something else, default value is used when the value is not set explicitly. – Utku Özdemir Feb 26 '14 at 10:17
  • Maybe set them in a static initializer/constructor to have declaration and initialization separated? I normally keep it this way. – Roland Nov 02 '17 at 11:29
274

Actually it is possible in JPA, although a little bit of a hack using the columnDefinition property of the @Column annotation, for example:

@Column(name="Price", columnDefinition="Decimal(10,2) default '100.00'")
Jasper
  • 2,166
  • 4
  • 30
  • 50
Cameron Pope
  • 7,565
  • 2
  • 26
  • 24
  • Decimal(10,2), what does 10 and 2 stand for? – Schildmeijer Apr 09 '09 at 12:42
  • 3
    10 is the Integer part, and 2 is the decimal part of the number so: 1234567890.12 would be a supported number. – Nathan Feger May 08 '09 at 14:50
  • 27
    Note also, this columnDefinition is not necessarily database independent, and is certainly not automatically tied the the datatype in Java. – Nathan Feger May 08 '09 at 14:50
  • 57
    After creating an entity with a null field and persisting it, you wouldn't have the value set in it. Not a solution... – Pascal Thivent Jul 11 '10 at 21:59
  • 21
    No @NathanFeger, 10 is the length and 2 the decimal part. So 1234567890.12 woudn't be a supported number, but 12345678.90 is valid. – GPrimola Mar 11 '14 at 14:17
  • @GPrimola Thanks for pointing that very invaluable fact out, man I almost went bald wondering why my decimal `50.22` was giving a data truncation error in a `DECIMAL(2,2)` column definition. +1 – qualebs Mar 18 '14 at 13:20
  • 1
    Although not database independent, the benefit of using this approach is when the column is added after the app is deployed in production (ddl auto update on). This way existing row will have the default value filled in on database upgrade – gerrytan Nov 07 '14 at 00:21
  • 7
    You would need `insertable=false` if the column is nullable (and to avoid the unneeded column argument). – eckes Aug 07 '15 at 01:11
  • The generic JPA (`javax.persistence` packages) is cross-JPA-implementation **and** cross-SQL-dialect/DBMS. Not all may understand e.g. `DEFAULT CURRENT_TIMESTAMP`. `columnDefinition` may be obsolete in favor of above said cross-independencies. – Roland Oct 13 '17 at 22:19
  • 1
    When creating new column to the existing table, this is best way! – Sherzod Jun 07 '21 at 13:05
128

another approach is using javax.persistence.PrePersist

@PrePersist
void preInsert() {
   if (this.createdTime == null)
       this.createdTime = new Date();
}
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
Husin Wijaya
  • 1,401
  • 1
  • 10
  • 6
  • 2
    I used an approach like this for adding and updating timestamps. It worked very well. – Spina Aug 14 '13 at 20:43
  • 10
    Shouldn't this be `if (createdt != null) createdt = new Date();` or something? Right now, this will override an explicitly specified value, which seems to make it not really be a default. – Max Nanasy Nov 25 '14 at 21:48
  • 19
    @MaxNanasy `if (createdt == null) createdt = new Date();` – Shane Dec 16 '14 at 20:46
  • Would it matter if the client and database are in different timezones? I imagine that using something like "TIMESTAMP DEFAULT CURRENT_TIMESTAMP" would get the current time on the database server, and "createdt = new Date()" would get the current time in the java code, but these two times might not be the same if the client is connecting to a server in a different timezone. – FrustratedWithFormsDesigner Mar 16 '15 at 15:50
  • Added the `null` check. – Ondra Žižka Oct 02 '17 at 04:18
  • 1
    I like this b/c it alows for new objects to have null values at first instead of the DB default. – Adam B Mar 01 '19 at 13:29
  • If you also need to put the default value when updating the record, add the `@PreUpdate` annotation too alongside the `@PrePersist` annotation. – Mirza Prangon Aug 30 '23 at 08:45
66

In 2017, JPA 2.1 still has only @Column(columnDefinition='...') to which you put the literal SQL definition of the column. Which is quite unflexible and forces you to also declare the other aspects like type, short-circuiting the JPA implementation's view on that matter.

Hibernate though, has this:

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

Identifies the DEFAULT value to apply to the associated column via DDL.

Two notes to that:

1) Don't be afraid of going non-standard. Working as a JBoss developer, I've seen quite some specification processes. The specification is basically the baseline that the big players in given field are willing to commit to support for the next decade or so. It's true for security, for messaging, ORM is no difference (although JPA covers quite a lot). My experience as a developer is that in a complex application, sooner or later you will need a non-standard API anyway. And @ColumnDefault is an example when it outweigts the negatives of using a non-standard solution.

2) It's nice how everyone waves @PrePersist or constructor member initialization. But that's NOT the same. How about bulk SQL updates? How about statements that don't set the column? DEFAULT has it's role and that's not substitutable by initializing a Java class member.

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
14

JPA doesn't support that and it would be useful if it did. Using columnDefinition is DB-specific and not acceptable in many cases. setting a default in the class is not enough when you retrieve a record having null values (which typically happens when you re-run old DBUnit tests). What I do is this:

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 auto-boxing helps a lot in that.

Tim Stone
  • 19,119
  • 6
  • 56
  • 66
Marco
  • 71
  • 2
  • 2
  • Don't abuse setters! They are for setting a parameter in an object field. Nothing more! Better use default constructor for default values. – Roland Oct 13 '17 at 22:13
  • @Roland nice in theory, but won't always work when dealing with an existing database that already contains null values where your application would like to not have them. You'd need to do a database conversion first where you make the column not-null and set a sensible default at the same time, and then deal with the backlash from other applications that assume it is in fact nullable (that is, if you can even modify the database). – jwenting Sep 04 '19 at 10:29
  • If it comes to the #JPA and setters/getters, they must always be pure setter/getter else one day your application has grown and grown and becomes a maintenance nightmare. Best advice is KISS here (I'm no gay, LOL). – Roland Nov 22 '19 at 02:25
10
@Column(columnDefinition="tinyint(1) default 1")

I just tested the issue. It works just fine. Thanks for the hint.


About the comments:

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

This one doesn't set the default column value in the database (of course).

Stu Thompson
  • 38,370
  • 19
  • 110
  • 156
asd
  • 101
  • 1
  • 2
  • 11
    **It deosn't work fine** at the object level (you won't get the database default value after an insert in your entity). Default at the Java level. – Pascal Thivent Jul 12 '10 at 10:14
  • It works (especially if you specify insertable=false into the database, but unfortunatelly the cached version is not refreshed (not even when JPA selects the table and columns). At least not with hibernate :-/ but even it if would work the additional roundtrip is bad. – eckes Aug 07 '15 at 01:05
10

Seeing as I stumbled upon this from Google while trying to solve the very same problem, I'm just gonna throw in the solution I cooked up in case someone finds it useful.

From my point of view there's really only 1 solutions to this problem -- @PrePersist. If you do it in @PrePersist, you gotta check if the value's been set already though.

TC1
  • 1
  • 3
  • 20
  • 31
  • 4
    +1 - I'd definitely opt for `@PrePersist` for OP's usecase. `@Column(columnDefinition=...)` doesn't seem very elegant. – Piotr Nowicki Jun 08 '12 at 11:57
  • 1
    @PrePersist won't help you if there already is data in the store with null values. It won't magically do a database conversion for you. – jwenting Sep 04 '19 at 10:30
10

I use columnDefinition and it works very good

@Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

private Date createdDate;
Udo Held
  • 12,314
  • 11
  • 67
  • 93
Tong
  • 101
  • 1
  • 2
  • 7
    This looks like it makes your jpa implementation vendor specific. – Udo Held Jan 05 '12 at 21:54
  • It only makes the DDL vendor specific. But you have most likely vendor specific DDL hacks anyway.. However as mentioned above the value (even when insertable=false) shows up in the DB but not in the entity cache of the session (at least not in hibernate). – eckes Aug 07 '15 at 01:07
9

you can use the java reflect api:

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

This is common:

    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();
            }
        }
    }
Thomas Zhang
  • 200
  • 2
  • 8
  • 2
    I like this solution, but have one point of weakness, I think that would be important check if the column is nullable or not before set default value. – Luis Carlos May 31 '17 at 07:44
  • If you prefer, putting the code from `Field[] fields = object.getClass().getDeclaredFields();` into the `for()` might be okay, too. And also add `final` to your parameter/caught exceptions as you don't want `object` being modified by accident. Also add a check on `null`: `if (null == object) { throw new NullPointerException("Parameter 'object' is null"); }`. That makes sure that `object.getClass()` is safe to invoke and does not trigger a `NPE`. The reason is to avoid mistakes by lazy programmers. ;-) – Roland Nov 02 '17 at 11:23
9
  1. @Column(columnDefinition='...') doesn't work when you set the default constraint in database while inserting the data.
  2. You need to make insertable = false and remove columnDefinition='...' from annotation, then database will automatically insert the default value from the database.
  3. E.g. when you set varchar gender is male by default in database.
  4. You just need to add insertable = false in Hibernate/JPA, it will work.
bluish
  • 26,356
  • 27
  • 122
  • 180
Appesh
  • 374
  • 4
  • 6
7

You can't do this with the column annotation. I think the only way is to set the default value when a object is created. Maybe the default constructor would be the right place to do that.

Piotr Nowicki
  • 17,914
  • 8
  • 63
  • 82
Timo
  • 597
  • 1
  • 6
  • 11
  • Nice idea. Sadly there is no generic annotation or attribute for `@Column` around. And I also miss comments being set (taken from Java doctag). – Roland Oct 13 '17 at 22:09
5

In my case, I modified hibernate-core source code, well, to introduce a new annotation @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 );

Well, this is a hibernate-only solution.

Lenik
  • 13,946
  • 17
  • 75
  • 103
  • 2
    While I do appreciate the effort of people that actively contribute to open source project, I downvoted this answer because JPA is a standard Java specification on top of any OR/M, the OP asked for a JPA way to specify default values and your patch works only for Hibernate. If this was NHibernate, in which there is no *super-partes* specification for persistance (NPA is not even supported by MS EF), I would have upvoted such kind of patch. The truth is that JPA is quite limited compared to the requirements of ORM (one example: no secondary indexes). Anyway kudos to the effort – usr-local-ΕΨΗΕΛΩΝ Aug 02 '13 at 07:57
  • Does this not create a maintenance issue? One would need to redo these changes whenever hibernate upgrades or on every fresh installation? – user1242321 May 10 '16 at 01:43
  • Since the question is about JPA, and Hibernate isn't even mentioned, this doesn't answer the question – Neil Stockton May 05 '17 at 10:22
3
@PrePersist
void preInsert() {
    if (this.dateOfConsent == null)
        this.dateOfConsent = LocalDateTime.now();
    if(this.consentExpiry==null)
        this.consentExpiry = this.dateOfConsent.plusMonths(3);
}

In my case due to the field being LocalDateTime i used this, it is recommended due to vendor independence

Mohammed Rafeeq
  • 2,586
  • 25
  • 26
3

I found another way to resolve the same problem, because when I create my own object and persist in database and didn´t respect the DDL with default value.

So I looked at my console, and the SQL generated, and saw that insert came with all fields, but only one propertie in my object has the value changed.

So I put in the model class this annotation.

@DynamicInsert

When is inserting data, the framework not insert null values or values that are not modified, making the insert shorter.

Also has @DynamicUpdate annotation.

Diego Macario
  • 1,240
  • 2
  • 21
  • 33
2

This isn't possible in JPA.

Here's what you can do with the Column annotation: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html

PEELY
  • 1,183
  • 1
  • 11
  • 21
2

If you're using a double, you can use the following:

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

private Double grolsh;

Yes it's db specific.

Gal Bracha
  • 19,004
  • 11
  • 72
  • 86
1

Neither JPA nor Hibernate annotations support the notion of a default column value. As a workaround to this limitation, set all default values just before you invoke a Hibernate save() or update() on the session. This closely as possible (short of Hibernate setting the default values) mimics the behaviour of the database which sets default values when it saves a row in a table.

Unlike setting the default values in the model class as this alternative answer suggests, this approach also ensures that criteria queries that use an Example object as a prototype for the search will continue to work as before. When you set the default value of a nullable attribute (one that has a non-primitive type) in a model class, a Hibernate query-by-example will no longer ignore the associated column where previously it would ignore it because it was null.

Community
  • 1
  • 1
Derek Mahar
  • 27,608
  • 43
  • 124
  • 174
  • In previous solution author menthioned ColumnDefault("") – nikolai.serdiuk May 18 '17 at 16:40
  • @nikolai.serdiuk that ColumnDefault annotation was added years after this answer was written. In 2010 it was correct, there was no such annotation (in fact there were no annotations, only xml configuration). – jwenting Sep 04 '19 at 10:32
1

You can define the default value in the database designer, or when you create the table. For instance in SQL Server you can set the default vault of a Date field to (getDate()). Use insertable=false as mentioned in your column definition. JPA will not specify that column on inserts and the database will generate the value for you.

bluish
  • 26,356
  • 27
  • 122
  • 180
0

I tried a couple of JPA/Hiberate ways but none seemed to work well. Since I am using Oracle I create a "before trigger" within the trigger a simple test of null then if null set as needed

Oxnard
  • 237
  • 1
  • 6
  • 18
0
@ColumnDefault("abcd")
var name: String,

There! you have set the default value for column name

Akash Verma
  • 638
  • 1
  • 11
  • 15