make @Check work when applied at the field level
This was a bug! Also add checkConstraint member to @Table to allow check constraints on secondary tables Also clean up some Javadoc of some related annotations
This commit is contained in:
parent
bd8bf15e00
commit
596debed4d
|
@ -12,7 +12,8 @@ import jakarta.persistence.PersistenceException;
|
|||
|
||||
import org.hibernate.annotations.Check;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.PostgreSQL81Dialect;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
|
@ -26,7 +27,8 @@ import static org.junit.Assert.fail;
|
|||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(PostgreSQL81Dialect.class)
|
||||
@RequiresDialect(PostgreSQLDialect.class)
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
public class CheckTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
|
@ -81,7 +83,6 @@ public class CheckTest extends BaseEntityManagerFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Check(constraints = "code > 0")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
|
@ -89,8 +90,7 @@ public class CheckTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
private String name;
|
||||
|
||||
// This one does not work! Only the entity-level annotation works.
|
||||
// @Check(constraints = "code > 0")
|
||||
@Check(constraints = "code > 0")
|
||||
private Long code;
|
||||
|
||||
public Long getId() {
|
||||
|
|
|
@ -15,7 +15,11 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Arbitrary SQL CHECK constraints which can be defined at the class, property or collection level.
|
||||
* Specifies a {@code check} constraint to be included in the generated DDL.
|
||||
* <ul>
|
||||
* <li>When a field or property is annotated, the check constraint is added to the column definition.
|
||||
* <li>When an entity class is annotated, the check constraint is added to the primary table.
|
||||
* </ul>
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
|
@ -23,7 +27,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
@Retention(RUNTIME)
|
||||
public @interface Check {
|
||||
/**
|
||||
* The check constraints string.
|
||||
* The check constraint, written in native SQL.
|
||||
*/
|
||||
String constraints();
|
||||
}
|
||||
|
|
|
@ -15,23 +15,24 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Define the foreign key name.
|
||||
* Specifies a foreign key name.
|
||||
*
|
||||
* @deprecated Prefer the JPA 2.1 introduced {@link jakarta.persistence.ForeignKey} instead.
|
||||
* @deprecated use the JPA 2.1 {@link jakarta.persistence.ForeignKey} annotation
|
||||
*/
|
||||
@Target({FIELD, METHOD, TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Deprecated
|
||||
public @interface ForeignKey {
|
||||
/**
|
||||
* Name of the foreign key. Used in OneToMany, ManyToOne, and OneToOne
|
||||
* relationships. Used for the owning side in ManyToMany relationships
|
||||
* Name of the foreign key of a {@code OneToMany}, {@code ManyToOne}, or
|
||||
* {@code OneToOne} association. May also be applied to the owning side a
|
||||
* {@code ManyToMany} association.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Used for the non-owning side of a ManyToMany relationship. Ignored
|
||||
* in other relationships
|
||||
* Used for the non-owning side of a {@code ManyToMany} association.
|
||||
* Ignored for other association cardinalities.
|
||||
*/
|
||||
String inverseName() default "";
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import static java.lang.annotation.ElementType.METHOD;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Define a DB index.
|
||||
* Defines an index of a database table.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*
|
||||
|
|
|
@ -14,82 +14,111 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Complementary information to a table either primary or secondary.
|
||||
* Complementary information for a table declared using the {@link jakarta.persistence.Table},
|
||||
* or {@link jakarta.persistence.SecondaryTable} annotation. Usually used only for secondary
|
||||
* tables.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*
|
||||
* @see jakarta.persistence.Table
|
||||
* @see jakarta.persistence.SecondaryTable
|
||||
*/
|
||||
@Target({TYPE})
|
||||
@Target(TYPE)
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(Tables.class)
|
||||
public @interface Table {
|
||||
/**
|
||||
* name of the targeted table.
|
||||
* The name of the targeted table.
|
||||
*/
|
||||
String appliesTo();
|
||||
|
||||
/**
|
||||
* Indexes.
|
||||
*
|
||||
* @deprecated use {@link jakarta.persistence.Table#indexes()} or
|
||||
* {@link jakarta.persistence.SecondaryTable#indexes()}
|
||||
*/
|
||||
@Deprecated
|
||||
Index[] indexes() default {};
|
||||
|
||||
/**
|
||||
* Define a table comment.
|
||||
* A check constraint, written in native SQL.
|
||||
*
|
||||
* @see Check
|
||||
*/
|
||||
String checkConstraint() default "";
|
||||
|
||||
/**
|
||||
* Specifies comment to add to the generated DDL for the table.
|
||||
*
|
||||
* @see Comment
|
||||
*/
|
||||
String comment() default "";
|
||||
|
||||
/**
|
||||
* Defines the Foreign Key name of a secondary table pointing back to the primary table.
|
||||
* Specifies a foreign key of a secondary table, which points back to the primary table.
|
||||
*
|
||||
* @deprecated use {@link jakarta.persistence.SecondaryTable#foreignKey()}
|
||||
*/
|
||||
@Deprecated
|
||||
ForeignKey foreignKey() default @ForeignKey( name="" );
|
||||
|
||||
/**
|
||||
* If set to JOIN, the default, Hibernate will use an inner join to retrieve a
|
||||
* secondary table defined by a class or its superclasses and an outer join for a
|
||||
* secondary table defined by a subclass.
|
||||
* If set to select then Hibernate will use a
|
||||
* sequential select for a secondary table defined on a subclass, which will be issued only if a row
|
||||
* turns out to represent an instance of the subclass. Inner joins will still be used to retrieve a
|
||||
* secondary defined by the class and its superclasses.
|
||||
*
|
||||
* <b>Only applies to secondary tables</b>
|
||||
* Defines a fetching strategy for the secondary table.
|
||||
* <ul>
|
||||
* <li>If set to {@link FetchMode#JOIN}, the default, Hibernate will use an inner join to
|
||||
* retrieve a secondary table defined by a class or its superclasses and an outer join for
|
||||
* a secondary table defined by a subclass.
|
||||
* <li>If set to {@link FetchMode#SELECT} then Hibernate will use a sequential select for
|
||||
* a secondary table defined on a subclass, which will be issued only if a row turns out
|
||||
* to represent an instance of the subclass. Inner joins will still be used to retrieve a
|
||||
* secondary table defined by the class and its superclasses.
|
||||
* </ul>
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*/
|
||||
FetchMode fetch() default FetchMode.JOIN;
|
||||
|
||||
/**
|
||||
* If true, Hibernate will not try to insert or update the properties defined by this join.
|
||||
*
|
||||
* <b>Only applies to secondary tables</b>
|
||||
* If enabled, Hibernate will never insert or update the columns of the secondary table.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*/
|
||||
boolean inverse() default false;
|
||||
|
||||
/**
|
||||
* If enabled, Hibernate will insert a row only if the properties defined by this join are non-null
|
||||
* and will always use an outer join to retrieve the properties.
|
||||
*
|
||||
* <b>Only applies to secondary tables</b>
|
||||
* If enabled, Hibernate will insert a row only if the columns of the secondary table
|
||||
* would not all be null, and will always use an outer join to read the columns. Thus,
|
||||
* by default, Hibernate avoids creating a row of null values.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.<p></em>
|
||||
*/
|
||||
boolean optional() default true;
|
||||
|
||||
/**
|
||||
* Defines a custom SQL insert statement.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*
|
||||
* <b>Only applies to secondary tables</b>
|
||||
* @see SQLInsert
|
||||
*/
|
||||
SQLInsert sqlInsert() default @SQLInsert(sql="");
|
||||
|
||||
/**
|
||||
* Defines a custom SQL update statement.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*
|
||||
* <b>Only applies to secondary tables</b>
|
||||
* @see SQLUpdate
|
||||
*/
|
||||
SQLUpdate sqlUpdate() default @SQLUpdate(sql="");
|
||||
|
||||
/**
|
||||
* Defines a custom SQL delete statement.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*
|
||||
* <b>Only applies to secondary tables</b>
|
||||
* @see SQLDelete
|
||||
*/
|
||||
SQLDelete sqlDelete() default @SQLDelete(sql="");
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Map;
|
|||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.annotations.Check;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.GeneratedColumn;
|
||||
import org.hibernate.annotations.ColumnTransformer;
|
||||
|
@ -73,6 +74,7 @@ public class AnnotatedColumn {
|
|||
private String generatedAs;
|
||||
|
||||
private String comment;
|
||||
private String checkConstraint;
|
||||
|
||||
public void setTable(Table table) {
|
||||
this.table = table;
|
||||
|
@ -193,10 +195,18 @@ public class AnnotatedColumn {
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
public String getCheckConstraint() {
|
||||
return checkConstraint;
|
||||
}
|
||||
|
||||
public void setDefaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public void setCheckConstraint(String checkConstraint) {
|
||||
this.checkConstraint = checkConstraint;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
@ -229,6 +239,9 @@ public class AnnotatedColumn {
|
|||
if ( defaultValue != null ) {
|
||||
mappingColumn.setDefaultValue( defaultValue );
|
||||
}
|
||||
if ( checkConstraint !=null ) {
|
||||
mappingColumn.setCheckConstraint( checkConstraint );
|
||||
}
|
||||
if ( StringHelper.isNotEmpty( comment ) ) {
|
||||
mappingColumn.setComment( comment );
|
||||
}
|
||||
|
@ -266,6 +279,7 @@ public class AnnotatedColumn {
|
|||
this.mappingColumn.setNullable( nullable );
|
||||
this.mappingColumn.setSqlType( sqlType );
|
||||
this.mappingColumn.setUnique( unique );
|
||||
this.mappingColumn.setCheckConstraint( checkConstraint );
|
||||
|
||||
if ( writeExpression != null ) {
|
||||
final int numberOfJdbcParams = StringHelper.count( writeExpression, '?' );
|
||||
|
@ -645,6 +659,7 @@ public class AnnotatedColumn {
|
|||
column.setBuildingContext( context );
|
||||
column.applyColumnDefault( inferredData, length );
|
||||
column.applyGeneratedAs( inferredData, length );
|
||||
column.applyCheckConstraint( inferredData, length );
|
||||
column.extractDataFromPropertyData(inferredData);
|
||||
column.bind();
|
||||
columns[index] = column;
|
||||
|
@ -685,7 +700,25 @@ public class AnnotatedColumn {
|
|||
}
|
||||
else {
|
||||
LOG.trace(
|
||||
"Could not perform @ColumnGeneratedAlways lookup as 'PropertyData' did not give access to XProperty"
|
||||
"Could not perform @GeneratedColumn lookup as 'PropertyData' did not give access to XProperty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyCheckConstraint(PropertyData inferredData, int length) {
|
||||
final XProperty xProperty = inferredData.getProperty();
|
||||
if ( xProperty != null ) {
|
||||
Check columnDefaultAnn = xProperty.getAnnotation( Check.class );
|
||||
if ( columnDefaultAnn != null ) {
|
||||
if (length!=1) {
|
||||
throw new MappingException("@Check may only be applied to single-column mappings (use a table-level @Check)");
|
||||
}
|
||||
setCheckConstraint( columnDefaultAnn.constraints() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG.trace(
|
||||
"Could not perform @Check lookup as 'PropertyData' did not give access to XProperty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -761,6 +794,7 @@ public class AnnotatedColumn {
|
|||
column.setImplicit( implicit );
|
||||
column.applyColumnDefault( inferredData, 1 );
|
||||
column.applyGeneratedAs( inferredData, 1 );
|
||||
column.applyCheckConstraint( inferredData, 1 );
|
||||
column.extractDataFromPropertyData( inferredData );
|
||||
column.bind();
|
||||
|
||||
|
|
|
@ -894,18 +894,19 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
|
|||
* @param column the referenced column.
|
||||
*/
|
||||
public void overrideFromReferencedColumnIfNecessary(Column column) {
|
||||
if (getMappingColumn() != null) {
|
||||
Column mappingColumn = getMappingColumn();
|
||||
if (mappingColumn != null) {
|
||||
// columnDefinition can also be specified using @JoinColumn, hence we have to check
|
||||
// whether it is set or not
|
||||
if ( StringHelper.isEmpty( sqlType ) ) {
|
||||
sqlType = column.getSqlType();
|
||||
getMappingColumn().setSqlType( sqlType );
|
||||
mappingColumn.setSqlType( sqlType );
|
||||
}
|
||||
|
||||
// these properties can only be applied on the referenced column - we can just take them over
|
||||
getMappingColumn().setLength(column.getLength());
|
||||
getMappingColumn().setPrecision(column.getPrecision());
|
||||
getMappingColumn().setScale(column.getScale());
|
||||
mappingColumn.setLength( column.getLength() );
|
||||
mappingColumn.setPrecision( column.getPrecision() );
|
||||
mappingColumn.setScale( column.getScale() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1230,6 +1230,9 @@ public class EntityBinder {
|
|||
if ( !BinderHelper.isEmptyAnnotationValue( table.comment() ) ) {
|
||||
hibTable.setComment( table.comment() );
|
||||
}
|
||||
if ( !BinderHelper.isEmptyAnnotationValue( table.checkConstraint() ) ) {
|
||||
hibTable.addCheckConstraint( table.checkConstraint() );
|
||||
}
|
||||
TableBinder.addIndexes( hibTable, table.indexes(), context );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue