example anotaciones java jpa annotations

java - anotaciones - Configuración de valores por defecto para columnas en JPA



jpa anotaciones (18)

¿Es posible establecer un valor predeterminado para las columnas en JPA, y si, cómo se hace con las anotaciones?


  1. @Column(columnDefinition=''...'') no funciona cuando configura la restricción predeterminada en la base de datos al insertar los datos.
  2. columnDefinition=''...'' hacer insertable = false y eliminar columnDefinition=''...'' de la anotación, luego la base de datos insertará automáticamente el valor predeterminado de la base de datos.
  3. Por ejemplo, cuando establece varchar gender es masculino por defecto en la base de datos.
  4. Solo necesita agregar insertable = false en Hibernate / JPA, funcionará.

En 2017, JPA todavía tiene solo @Column(columnDefinition=''...'') en la que coloca la definición de SQL literal de la columna. Lo que es bastante poco flexible y lo obliga a declarar también otros aspectos como el tipo, cortocircuitando la opinión de la implementación de JPA sobre ese asunto.

Hibernate sin embargo, tiene esto:

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

Identifica el valor POR DEFECTO para aplicar a la columna asociada a través de DDL.

Dos notas a eso:

1) No tengas miedo de no ser estándar. Trabajando como desarrollador de JBoss, he visto algunos procesos de especificación. La especificación es básicamente la línea de base que los grandes jugadores en un determinado campo están dispuestos a comprometerse a apoyar durante la próxima década. Es cierto para la seguridad, para los mensajes, ORM no es una diferencia (aunque JPA cubre bastante). Mi experiencia como desarrollador es que en una aplicación compleja, tarde o temprano necesitarás una API no estándar de todos modos. Y @ColumnDefault es un ejemplo cuando supera los aspectos negativos de usar una solución no estándar.

2) Es bueno cómo todos ondean @PrePersist o la inicialización de miembros constructores. Pero eso NO es lo mismo. ¿Qué tal actualizaciones masivas de SQL? ¿Qué hay de las declaraciones que no establecen la columna? DEFAULT tiene su rol y eso no es sustituible al inicializar el miembro de la clase Java.


En mi caso, modifiqué el código fuente de hibernate-core, bueno, para introducir una nueva anotación @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 谢继雷 + */ [email protected]({ 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 );

Bueno, esta es una solución sólo para hibernación.



JPA no apoya eso y sería útil si lo hiciera. El uso de columnDefinition es específico de DB y no es aceptable en muchos casos. establecer un valor predeterminado en la clase no es suficiente cuando recupera un registro con valores nulos (lo que suele suceder cuando se vuelven a ejecutar las antiguas pruebas de DBUnit). Lo que hago es esto:

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 ayuda mucho en eso.



Ni las anotaciones JPA ni Hibernate admiten la noción de un valor de columna predeterminado. Como solución a esta limitación, configure todos los valores predeterminados justo antes de invocar un save() o una update() Hibernate en la sesión. Esto lo más cerca posible (salvo que Hibernate haya establecido los valores predeterminados) imita el comportamiento de la base de datos que establece los valores predeterminados cuando guarda una fila en una tabla.

A diferencia de configurar los valores predeterminados en la clase del modelo como sugiere esta respuesta alternativa , este enfoque también garantiza que las consultas de criterios que usan un objeto Example como prototipo para la búsqueda continuarán funcionando como antes. Cuando establece el valor predeterminado de un atributo anulable (uno que tiene un tipo no primitivo) en una clase modelo, una consulta por ejemplo de Hibernación ya no ignorará la columna asociada donde anteriormente lo ignoraría porque era nulo.


No se puede hacer esto con la anotación de columna. Creo que la única forma es establecer el valor predeterminado cuando se crea un objeto. Tal vez el constructor por defecto sería el lugar correcto para hacerlo.


Puede definir el valor predeterminado en el diseñador de la base de datos o al crear la tabla. Por ejemplo, en SQL Server, puede establecer la bóveda predeterminada de un campo de fecha en ( getDate() ). Use insertable=false como se menciona en la definición de su columna. JPA no especificará esa columna en las inserciones y la base de datos generará el valor para usted.


Puedes hacer lo siguiente:

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

¡Ahí! Acaba de usar cero como valor predeterminado.

Tenga en cuenta que esto le servirá si solo está accediendo a la base de datos desde esta aplicación. Si otras aplicaciones también utilizan la base de datos, debe realizar esta comprobación desde la base de datos utilizando Cameron''s atributo de anotación columnDefinition Cameron''s , o de alguna otra forma.


Si está usando un doble, puede usar lo siguiente:

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

Sí es db específico.


Uso columnDefinition y funciona muy bien.

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


Viendo que me topé con esto en Google mientras intentaba resolver el mismo problema, solo voy a lanzar la solución que preparé en caso de que alguien lo encuentre útil.

Desde mi punto de vista, realmente hay solo 1 soluciones para este problema: @PrePersist. Si lo haces en @PrePersist, debes verificar si el valor ya se ha establecido.


otro enfoque es usar javax.persistence.PrePersist

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


puedes usar el java reflect api:

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

Esto es común:

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


En realidad, es posible en JPA, aunque un poco de columnDefinition usando la propiedad columnDefinition de la anotación @Column , por ejemplo:

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


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

Acabo de probar el problema. Funciona bien Gracias por la pista.

Sobre los comentarios:

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

Este no establece el valor de columna predeterminado en la base de datos (por supuesto).


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

En mi caso, debido a que el campo es LocalDateTime, utilicé esto, se recomienda debido a la independencia del proveedor.