Impostazione dei valori predefiniti per le colonne in JPA

È ansible impostare un valore predefinito per le colonne in JPA e, in che modo è ansible utilizzare le annotazioni?

In realtà è ansible in JPA, anche se un po ‘di un hack che usa la proprietà @Column dell’annotazione @Column , ad esempio:

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

Puoi fare quanto segue:

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

Là! Hai appena usato zero come valore predefinito.

Nota che questo ti sarà utile se accedi al database solo da questa applicazione. Se anche altre applicazioni utilizzano il database, è necessario effettuare questo controllo dal database utilizzando l’ attributo di annotazione columnDefinition di Cameron o in un altro modo.

un altro approccio sta usando javax.persistence.PrePersist

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

Nel 2017, JPA ha ancora solo @Column(columnDefinition='...') a cui si mette la definizione letterale SQL della colonna. Il che è alquanto rigido e costringe a dichiarare anche altri aspetti come il tipo, mettendo in cortocircuito la visione dell’implementazione dell’APP su tale questione.

Hibernate però, ha questo:

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

Identifica il valore DEFAULT da applicare alla colonna associata tramite DDL.

Hibernate 4.3 doc (4.3 per mostrare che è qui da parecchio tempo)
Manuale di ibernazione su Valore predefinito per la colonna del database

Due note a questo:

1) Non aver paura di non essere standard. Lavorando come sviluppatore JBoss, ho visto alcuni processi specifici. La specifica è fondamentalmente la linea di base che i grandi giocatori in questo campo sono disposti a impegnarsi a sostenere per il prossimo decennio. È vero per sicurezza, per la messaggistica, ORM non fa differenza (sebbene il JPA copra parecchio). La mia esperienza come sviluppatore è che in un’applicazione complessa, prima o poi avrai comunque bisogno di un’API non standard. E @ColumnDefault è un esempio quando supera i negativi della soluzione standard.

2) È bello il modo in cui tutti inviano l’inizializzazione di @PrePersist o del membro del costruttore. Ma non è la stessa cosa. Che ne dici di aggiornamenti SQL di massa? Che ne dici di affermazioni che non impostano la colonna? DEFAULT ha il suo ruolo e non è sostituibile inizializzando un membro della class Java.

JPA non lo supporta e sarebbe utile se lo facesse. L’utilizzo di columnDefinition è specifico per DB e non è accettabile in molti casi. l’impostazione di un valore predefinito nella class non è sufficiente quando si recupera un record con valori nulli (che di solito accade quando si eseguono nuovamente i vecchi test DBUnit). Quello che faccio è questo:

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

L’auto-boxing Java aiuta molto in questo.

Visto che sono incappato in questo da Google mentre cercavo di risolvere lo stesso problema, ho intenzione di gettare la soluzione che ho preparato nel caso qualcuno lo trovasse utile.

Dal mio punto di vista ci sono davvero solo 1 soluzioni a questo problema – @PrePersist. Se lo fai in @PrePersist, devi controllare se il valore è già stato impostato.

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

Ho appena testato il problema. Funziona bene. Grazie per il suggerimento.


A proposito dei commenti:

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

Questo non imposta il valore di colonna predefinito nel database (ovviamente).

Io uso columnDefinition e funziona molto bene

 @Column(columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP") private Date createdDate; 

Non puoi farlo con l’annotazione della colonna. Penso che l’unico modo è quello di impostare il valore predefinito quando viene creato un object. Forse il costruttore predefinito sarebbe il posto giusto per farlo.

puoi usare java reflect api:

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

Questo è comune:

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

Nel mio caso, ho modificato il codice sorgente di hibernate-core, beh, per introdurre una nuova annotazione @DefaultValue :

 commit 34199cba96b6b1dc42d0d19c066bd4d119b553d5 Author: Lenik  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 谢继雷+ */ [email protected]({ FIELD, METHOD }) [email protected](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.ColumnTransformsr; import org.hibernate.annotations.ColumnTransformsrs; +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 ); 

Bene, questa è una soluzione solo ibernata.

Né le annotazioni JPA né quelle di Hibernate supportano la nozione di un valore di colonna predefinito. Come soluzione alternativa a questa limitazione, imposta tutti i valori predefiniti appena prima di richiamare un Hibernate save() o update() sulla sessione. Il più vicino ansible (a eccezione di Hibernate che imposta i valori predefiniti) simula il comportamento del database che imposta i valori predefiniti quando salva una riga in una tabella.

A differenza dell’impostazione dei valori predefiniti nella class del modello come suggerisce questa risposta alternativa , questo approccio garantisce anche che le query di criteri che utilizzano un object Example come prototipo per la ricerca continueranno a funzionare come prima. Quando si imposta il valore predefinito di un attributo nullable (uno che ha un tipo non primitivo) in una class del modello, un Hibernate query-by-example non ignorerà più la colonna associata dove in precedenza lo ignorerebbe perché era null.

Questo non è ansible in JPA.

Ecco cosa puoi fare con l’annotazione Column: http://java.sun.com/javaee/5/docs/api/javax/persistence/Column.html

Se stai utilizzando un doppio, puoi utilizzare quanto segue:

 @Column(columnDefinition="double precision default '96'") private Double grolsh; 

Sì, è db specifico.

  1. @Column(columnDefinition='...') non funziona quando si imposta il vincolo predefinito nel database durante l’inserimento dei dati.
  2. Devi rendere insertable = false e rimuovere columnDefinition='...' dall’annotazione, quindi il database inserirà automaticamente il valore predefinito dal database.
  3. Ad esempio, quando si imposta varchar, il sesso è di default maschile nel database.
  4. Hai solo bisogno di aggiungere insertable = false in Hibernate / JPA, funzionerà.
 @PrePersist void preInsert() { if (this.dateOfConsent == null) this.dateOfConsent = LocalDateTime.now(); if(this.consentExpiry==null) this.consentExpiry = this.dateOfConsent.plusMonths(3); } 

Nel mio caso a causa del fatto che il campo è LocalDateTime ho usato questo, è consigliato a causa dell’indipendenza del venditore

È ansible definire il valore predefinito nella finestra di progettazione del database o quando si crea la tabella. Ad esempio in SQL Server è ansible impostare il deposito predefinito di un campo Data su ( getDate() ). Usa insertable=false come indicato nella definizione della colonna. JPA non specificherà quella colonna sugli inserti e il database genererà il valore per te.

Devi insertable=false nella tua annotazione @Column . JPA ignorerà quindi quella colonna durante l’inserimento nel database e verrà utilizzato il valore predefinito.

Vedi questo link: http://mariemjabloun.blogspot.com/2014/03/resolved-set-database-default-value-in.html