automatically detect when a @Check refers to a @SecondaryTable
- also support named check constraints (multiple of them) - also support check constraints on collection tables
This commit is contained in:
parent
abb89a32b1
commit
1657c22aca
|
@ -6,10 +6,12 @@
|
|||
*/
|
||||
package org.hibernate.userguide.schema;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
import jakarta.persistence.SecondaryTable;
|
||||
import org.hibernate.annotations.Check;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
@ -20,6 +22,8 @@ import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
|||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -120,7 +124,9 @@ public class CheckTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
//tag::schema-generation-database-checks-example[]
|
||||
@Entity(name = "Book")
|
||||
@Check(constraints = "CASE WHEN isbn IS NOT NULL THEN LENGTH(isbn) = 13 ELSE true END")
|
||||
@Check(name = "ValidIsbn", constraints = "CASE WHEN isbn IS NOT NULL THEN LENGTH(isbn) = 13 ELSE true END")
|
||||
@SecondaryTable(name = "BookEdition")
|
||||
@Check(name = "PositiveEdition", constraints = "edition > 0")
|
||||
public static class Book {
|
||||
|
||||
@Id
|
||||
|
@ -133,6 +139,12 @@ public class CheckTest extends BaseEntityManagerFunctionalTestCase {
|
|||
|
||||
private Double price;
|
||||
|
||||
@Column(table = "BookEdition")
|
||||
private int edition = 1;
|
||||
|
||||
@Column(table = "BookEdition")
|
||||
private LocalDate editionDate;
|
||||
|
||||
//Getters and setters omitted for brevity
|
||||
|
||||
//end::schema-generation-database-checks-example[]
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.annotations;
|
||||
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
@ -17,8 +18,19 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
/**
|
||||
* 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.
|
||||
* <li>When a basic-typed field or property is annotated, the check constraint is
|
||||
* added to the {@linkplain jakarta.persistence.Column column} definition.
|
||||
* <li>When a {@link jakarta.persistence.ManyToOne} association is annotated, the
|
||||
* check constraint is added to the {@linkplain jakarta.persistence.JoinColumn
|
||||
* join column} definition.
|
||||
* <li>When an owned collection is annotated, the check constraint is added to the
|
||||
* {@linkplain jakarta.persistence.CollectionTable collection table} or
|
||||
* {@linkplain jakarta.persistence.JoinTable association join table}.
|
||||
* <li>When an entity class is annotated, the check constraint is added to either
|
||||
* the {@linkplain jakarta.persistence.Table primary table} or to a
|
||||
* {@linkplain jakarta.persistence.SecondaryTable secondary table}, depending
|
||||
* on which columns are involved in the constraint expression specified by
|
||||
* {@link #constraints()}.
|
||||
* </ul>
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
|
@ -27,7 +39,12 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
*/
|
||||
@Target({TYPE, METHOD, FIELD})
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(Checks.class)
|
||||
public @interface Check {
|
||||
/**
|
||||
* The optional name of the check constraint.
|
||||
*/
|
||||
String name() default "";
|
||||
/**
|
||||
* The check constraint, written in native SQL.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* A list of {@link Check}s.
|
||||
*/
|
||||
@Target(TYPE)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Checks {
|
||||
Check[] value();
|
||||
}
|
|
@ -22,10 +22,16 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
*
|
||||
* @see jakarta.persistence.Table
|
||||
* @see jakarta.persistence.SecondaryTable
|
||||
*
|
||||
* @deprecated The options available here are all now offered by other newer and better-designed
|
||||
* annotations in this package. This annotation will soon be removed, since it's very
|
||||
* annoying to have two annotations named {@code @Table}.
|
||||
*
|
||||
*/
|
||||
@Target(TYPE)
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(Tables.class)
|
||||
@Deprecated(since = "6.2", forRemoval = true)
|
||||
public @interface Table {
|
||||
/**
|
||||
* The name of the targeted table.
|
||||
|
@ -43,11 +49,10 @@ public @interface Table {
|
|||
|
||||
/**
|
||||
* A check constraint, written in native SQL.
|
||||
* <p>
|
||||
* <em>Useful for secondary tables, otherwise use {@link Check}.</em>
|
||||
*
|
||||
* @see Check
|
||||
* @deprecated use {@link Check}.
|
||||
*/
|
||||
@Deprecated(since = "6.2")
|
||||
String checkConstraint() default "";
|
||||
|
||||
/**
|
||||
|
@ -61,6 +66,7 @@ public @interface Table {
|
|||
/**
|
||||
* Specifies a foreign key of a secondary table, which points back to the primary table.
|
||||
*
|
||||
* @apiNote Only relevant to secondary tables
|
||||
* @deprecated use {@link jakarta.persistence.SecondaryTable#foreignKey()}
|
||||
*/
|
||||
@Deprecated(since = "6.0", forRemoval = true)
|
||||
|
@ -74,9 +80,8 @@ public @interface Table {
|
|||
|
||||
/**
|
||||
* If enabled, Hibernate will never insert or update the columns of the secondary table.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*
|
||||
* @apiNote Only relevant to secondary tables
|
||||
* @deprecated use {@link SecondaryRow#owned()}
|
||||
*/
|
||||
@Deprecated(since = "6.2")
|
||||
|
@ -87,7 +92,7 @@ public @interface 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.
|
||||
*
|
||||
* @apiNote Only relevant for secondary tables
|
||||
* @apiNote Only relevant to secondary tables
|
||||
* @deprecated use {@link SecondaryRow#optional()}
|
||||
*/
|
||||
@Deprecated(since = "6.2")
|
||||
|
@ -95,9 +100,8 @@ public @interface Table {
|
|||
|
||||
/**
|
||||
* Defines a custom SQL insert statement.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*
|
||||
* @apiNote Only relevant to secondary tables
|
||||
* @deprecated use {@link SQLInsert#table()} to specify the secondary table
|
||||
*/
|
||||
@Deprecated(since="6.2")
|
||||
|
@ -105,9 +109,8 @@ public @interface Table {
|
|||
|
||||
/**
|
||||
* Defines a custom SQL update statement.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*
|
||||
* @apiNote Only relevant to secondary tables
|
||||
* @deprecated use {@link SQLInsert#table()} to specify the secondary table
|
||||
*/
|
||||
@Deprecated(since="6.2")
|
||||
|
@ -115,9 +118,8 @@ public @interface Table {
|
|||
|
||||
/**
|
||||
* Defines a custom SQL delete statement.
|
||||
* <p>
|
||||
* <em>Only applies to secondary tables.</em>
|
||||
*
|
||||
* @apiNote Only relevant to secondary tables
|
||||
* @deprecated use {@link SQLInsert#table()} to specify the secondary table
|
||||
*/
|
||||
@Deprecated(since="6.2")
|
||||
|
|
|
@ -16,9 +16,12 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
* A grouping of {@link Table}s.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*
|
||||
* @deprecated since {@link Table} is deprecated
|
||||
*/
|
||||
@Target(TYPE)
|
||||
@Retention(RUNTIME)
|
||||
@Deprecated(since = "6.2", forRemoval = true)
|
||||
public @interface Tables {
|
||||
/**
|
||||
* The table grouping.
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.hibernate.boot.spi.PropertyData;
|
|||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.mapping.AggregateColumn;
|
||||
import org.hibernate.mapping.CheckConstraint;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Formula;
|
||||
|
@ -83,6 +84,7 @@ public class AnnotatedColumn {
|
|||
private String generatedAs;
|
||||
|
||||
// private String comment;
|
||||
private String checkConstraintName;
|
||||
private String checkConstraint;
|
||||
|
||||
private AnnotatedColumns parent;
|
||||
|
@ -124,10 +126,6 @@ public class AnnotatedColumn {
|
|||
return isNotEmpty( formulaString );
|
||||
}
|
||||
|
||||
public String getFormulaString() {
|
||||
return formulaString;
|
||||
}
|
||||
|
||||
public String getExplicitTableName() {
|
||||
return explicitTableName;
|
||||
}
|
||||
|
@ -188,16 +186,13 @@ 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 void setCheckConstraint(String name, String constraint) {
|
||||
this.checkConstraintName = name;
|
||||
this.checkConstraint = constraint;
|
||||
}
|
||||
|
||||
// public String getComment() {
|
||||
|
@ -240,8 +235,8 @@ public class AnnotatedColumn {
|
|||
if ( defaultValue != null ) {
|
||||
mappingColumn.setDefaultValue( defaultValue );
|
||||
}
|
||||
if ( checkConstraint !=null ) {
|
||||
mappingColumn.setCheckConstraint( checkConstraint );
|
||||
if ( checkConstraint != null ) {
|
||||
mappingColumn.setCheck( new CheckConstraint( checkConstraintName, checkConstraint ) );
|
||||
}
|
||||
// if ( isNotEmpty( comment ) ) {
|
||||
// mappingColumn.setComment( comment );
|
||||
|
@ -280,7 +275,9 @@ public class AnnotatedColumn {
|
|||
mappingColumn.setNullable( nullable );
|
||||
mappingColumn.setSqlType( sqlType );
|
||||
mappingColumn.setUnique( unique );
|
||||
mappingColumn.setCheckConstraint( checkConstraint );
|
||||
if ( checkConstraint != null ) {
|
||||
mappingColumn.setCheck( new CheckConstraint( checkConstraintName, checkConstraint ) );
|
||||
}
|
||||
mappingColumn.setDefaultValue( defaultValue );
|
||||
|
||||
if ( writeExpression != null ) {
|
||||
|
@ -817,7 +814,7 @@ public class AnnotatedColumn {
|
|||
throw new AnnotationException("'@Check' may only be applied to single-column mappings but '"
|
||||
+ property.getName() + "' maps to " + length + " columns (use a table-level '@Check')" );
|
||||
}
|
||||
setCheckConstraint( check.constraints() );
|
||||
setCheckConstraint( check.name().isEmpty() ? null : check.name(), check.constraints() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -226,7 +226,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
|
|||
return join;
|
||||
}
|
||||
|
||||
private void addPropertyToPersistentClass(Property prop, XClass declaringClass) {
|
||||
private void addPropertyToPersistentClass(Property property, XClass declaringClass) {
|
||||
if ( declaringClass != null ) {
|
||||
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
|
||||
if ( inheritanceState == null ) {
|
||||
|
@ -235,15 +235,15 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
|
|||
);
|
||||
}
|
||||
if ( inheritanceState.isEmbeddableSuperclass() ) {
|
||||
persistentClass.addMappedSuperclassProperty( prop );
|
||||
addPropertyToMappedSuperclass( prop, declaringClass );
|
||||
persistentClass.addMappedSuperclassProperty( property );
|
||||
addPropertyToMappedSuperclass( property, declaringClass );
|
||||
}
|
||||
else {
|
||||
persistentClass.addProperty( prop );
|
||||
persistentClass.addProperty( property );
|
||||
}
|
||||
}
|
||||
else {
|
||||
persistentClass.addProperty( prop );
|
||||
persistentClass.addProperty( property );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,7 +359,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
|
|||
}
|
||||
}
|
||||
|
||||
private void addPropertyToJoin(Property prop, XClass declaringClass, Join join) {
|
||||
private void addPropertyToJoin(Property property, XClass declaringClass, Join join) {
|
||||
if ( declaringClass != null ) {
|
||||
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
|
||||
if ( inheritanceState == null ) {
|
||||
|
@ -368,15 +368,15 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
|
|||
);
|
||||
}
|
||||
if ( inheritanceState.isEmbeddableSuperclass() ) {
|
||||
join.addMappedsuperclassProperty(prop);
|
||||
addPropertyToMappedSuperclass( prop, declaringClass );
|
||||
join.addMappedsuperclassProperty( property );
|
||||
addPropertyToMappedSuperclass( property, declaringClass );
|
||||
}
|
||||
else {
|
||||
join.addProperty( prop );
|
||||
join.addProperty( property );
|
||||
}
|
||||
}
|
||||
else {
|
||||
join.addProperty( prop );
|
||||
join.addProperty( property );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.annotations.Bag;
|
|||
import org.hibernate.annotations.BatchSize;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.Cascade;
|
||||
import org.hibernate.annotations.Check;
|
||||
import org.hibernate.annotations.CollectionId;
|
||||
import org.hibernate.annotations.CollectionIdJavaType;
|
||||
import org.hibernate.annotations.CollectionIdJdbcType;
|
||||
|
@ -84,6 +85,7 @@ import org.hibernate.internal.CoreMessageLogger;
|
|||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.Backref;
|
||||
import org.hibernate.mapping.CheckConstraint;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Component;
|
||||
|
@ -1616,7 +1618,7 @@ public abstract class CollectionBinder {
|
|||
foreignJoinColumns.getBuildingContext(),
|
||||
inheritanceStatePerClass
|
||||
) );
|
||||
foreignJoinColumns.setJoins( joins);
|
||||
foreignJoinColumns.setJoins( joins );
|
||||
collection.setCollectionTable( foreignJoinColumns.getTable() );
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf( "Mapping collection: %s -> %s", collection.getRole(), collection.getCollectionTable().getName() );
|
||||
|
@ -1626,7 +1628,7 @@ public abstract class CollectionBinder {
|
|||
handleWhere( false );
|
||||
|
||||
final PersistentClass targetEntity = persistentClasses.get( getElementType().getName() );
|
||||
bindCollectionSecondPass( targetEntity, foreignJoinColumns, onDeleteAction);
|
||||
bindCollectionSecondPass( targetEntity, foreignJoinColumns );
|
||||
|
||||
if ( !collection.isInverse() && !collection.getKey().isNullable() ) {
|
||||
createOneToManyBackref( oneToMany );
|
||||
|
@ -2056,84 +2058,35 @@ public abstract class CollectionBinder {
|
|||
logManyToManySecondPass( oneToMany, isCollectionOfEntities, isManyToAny );
|
||||
|
||||
//check for user error
|
||||
detectManyToManyProblems(
|
||||
elementType,
|
||||
property,
|
||||
propertyHolder,
|
||||
isCollectionOfEntities,
|
||||
isManyToAny
|
||||
);
|
||||
detectManyToManyProblems( elementType, isCollectionOfEntities, isManyToAny );
|
||||
|
||||
if (isUnownedCollection()) {
|
||||
handleUnownedManyToMany(
|
||||
collection,
|
||||
joinColumns,
|
||||
elementType,
|
||||
targetEntity,
|
||||
isCollectionOfEntities
|
||||
);
|
||||
if ( isUnownedCollection() ) {
|
||||
handleUnownedManyToMany( elementType, targetEntity, isCollectionOfEntities );
|
||||
}
|
||||
else {
|
||||
handleOwnedManyToMany(
|
||||
collection,
|
||||
joinColumns,
|
||||
tableBinder,
|
||||
property,
|
||||
buildingContext,
|
||||
targetEntity,
|
||||
isCollectionOfEntities
|
||||
);
|
||||
handleOwnedManyToMany( targetEntity, isCollectionOfEntities );
|
||||
}
|
||||
bindFilters( isCollectionOfEntities );
|
||||
handleWhere( isCollectionOfEntities );
|
||||
|
||||
bindCollectionSecondPass( targetEntity, joinColumns, onDeleteAction);
|
||||
bindCollectionSecondPass( targetEntity, joinColumns );
|
||||
|
||||
if ( isCollectionOfEntities ) {
|
||||
final ManyToOne element = handleCollectionOfEntities(
|
||||
collection,
|
||||
elementType,
|
||||
notFoundAction,
|
||||
property,
|
||||
buildingContext,
|
||||
targetEntity,
|
||||
hqlOrderBy
|
||||
);
|
||||
final ManyToOne element = handleCollectionOfEntities( elementType, targetEntity, hqlOrderBy );
|
||||
bindManyToManyInverseForeignKey( targetEntity, inverseJoinColumns, element, oneToMany );
|
||||
}
|
||||
else if ( isManyToAny ) {
|
||||
handleManyToAny(
|
||||
collection,
|
||||
inverseJoinColumns,
|
||||
onDeleteAction,
|
||||
property,
|
||||
buildingContext
|
||||
);
|
||||
handleManyToAny();
|
||||
}
|
||||
else {
|
||||
handleElementCollection(
|
||||
collection,
|
||||
elementColumns,
|
||||
isEmbedded,
|
||||
elementType,
|
||||
property,
|
||||
propertyHolder,
|
||||
hqlOrderBy
|
||||
);
|
||||
handleElementCollection( elementType, hqlOrderBy );
|
||||
}
|
||||
|
||||
checkFilterConditions( collection );
|
||||
}
|
||||
|
||||
private void handleElementCollection(
|
||||
Collection collection,
|
||||
AnnotatedColumns elementColumns,
|
||||
boolean isEmbedded,
|
||||
XClass elementType,
|
||||
XProperty property,
|
||||
PropertyHolder parentPropertyHolder,
|
||||
String hqlOrderBy) {
|
||||
// 'parentPropertyHolder' is the PropertyHolder for the owner of the collection
|
||||
private void handleElementCollection(XClass elementType, String hqlOrderBy) {
|
||||
// 'propertyHolder' is the PropertyHolder for the owner of the collection
|
||||
// 'holder' is the CollectionPropertyHolder.
|
||||
// 'property' is the collection XProperty
|
||||
|
||||
|
@ -2141,7 +2094,7 @@ public abstract class CollectionBinder {
|
|||
final AnnotatedClassType classType = annotatedElementType( isEmbedded, property, elementType );
|
||||
final boolean primitive = classType == NONE;
|
||||
if ( !primitive ) {
|
||||
parentPropertyHolder.startingProperty( property );
|
||||
propertyHolder.startingProperty( property );
|
||||
}
|
||||
|
||||
final CollectionPropertyHolder holder = buildPropertyHolder(
|
||||
|
@ -2149,7 +2102,7 @@ public abstract class CollectionBinder {
|
|||
collection.getRole(),
|
||||
elementClass,
|
||||
property,
|
||||
parentPropertyHolder,
|
||||
propertyHolder,
|
||||
buildingContext
|
||||
);
|
||||
holder.prepare( property );
|
||||
|
@ -2157,18 +2110,15 @@ public abstract class CollectionBinder {
|
|||
final Class<? extends CompositeUserType<?>> compositeUserType =
|
||||
resolveCompositeUserType( property, elementClass, buildingContext );
|
||||
if ( classType == EMBEDDABLE || compositeUserType != null ) {
|
||||
handleCompositeCollectionElement( collection, property, hqlOrderBy, elementClass, holder, compositeUserType );
|
||||
handleCompositeCollectionElement( hqlOrderBy, elementClass, holder, compositeUserType );
|
||||
}
|
||||
else {
|
||||
handleCollectionElement( collection, elementColumns, elementType, property, hqlOrderBy, elementClass, holder );
|
||||
handleCollectionElement( elementType, hqlOrderBy, elementClass, holder );
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCollectionElement(
|
||||
Collection collection,
|
||||
AnnotatedColumns elementColumns,
|
||||
XClass elementType,
|
||||
XProperty property,
|
||||
String hqlOrderBy,
|
||||
XClass elementClass,
|
||||
CollectionPropertyHolder holder) {
|
||||
|
@ -2187,20 +2137,18 @@ public abstract class CollectionBinder {
|
|||
property,
|
||||
elementClass,
|
||||
collection.getOwnerEntityName(),
|
||||
holder.resolveElementAttributeConverterDescriptor(property, elementClass)
|
||||
holder.resolveElementAttributeConverterDescriptor( property, elementClass )
|
||||
);
|
||||
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
|
||||
elementBinder.setAccessType( accessType );
|
||||
collection.setElement( elementBinder.make() );
|
||||
final String orderBy = adjustUserSuppliedValueCollectionOrderingFragment(hqlOrderBy);
|
||||
final String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy );
|
||||
if ( orderBy != null ) {
|
||||
collection.setOrderBy( orderBy );
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCompositeCollectionElement(
|
||||
Collection collection,
|
||||
XProperty property,
|
||||
String hqlOrderBy,
|
||||
XClass elementClass,
|
||||
CollectionPropertyHolder holder,
|
||||
|
@ -2256,7 +2204,7 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
}
|
||||
|
||||
AnnotatedClassType annotatedElementType(
|
||||
private AnnotatedClassType annotatedElementType(
|
||||
boolean isEmbedded,
|
||||
XProperty property,
|
||||
XClass elementType) {
|
||||
|
@ -2301,14 +2249,7 @@ public abstract class CollectionBinder {
|
|||
return elementColumns;
|
||||
}
|
||||
|
||||
private ManyToOne handleCollectionOfEntities(
|
||||
Collection collection,
|
||||
XClass elementType,
|
||||
NotFoundAction notFoundAction,
|
||||
XProperty property,
|
||||
MetadataBuildingContext buildingContext,
|
||||
PersistentClass collectionEntity,
|
||||
String hqlOrderBy) {
|
||||
private ManyToOne handleCollectionOfEntities(XClass elementType, PersistentClass collectionEntity, String hqlOrderBy) {
|
||||
final ManyToOne element = new ManyToOne( buildingContext, collection.getCollectionTable() );
|
||||
collection.setElement( element );
|
||||
element.setReferencedEntityName( elementType.getName() );
|
||||
|
@ -2339,8 +2280,9 @@ public abstract class CollectionBinder {
|
|||
foreignKeyDefinition = joinColumnAnn.foreignKey().foreignKeyDefinition();
|
||||
}
|
||||
}
|
||||
if ( joinTableAnn.inverseForeignKey().value() == NO_CONSTRAINT
|
||||
|| joinTableAnn.inverseForeignKey().value() == PROVIDER_DEFAULT
|
||||
final ConstraintMode constraintMode = joinTableAnn.inverseForeignKey().value();
|
||||
if ( constraintMode == NO_CONSTRAINT
|
||||
|| constraintMode == PROVIDER_DEFAULT
|
||||
&& buildingContext.getBuildingOptions().isNoConstraintByDefault() ) {
|
||||
element.disableForeignKey();
|
||||
}
|
||||
|
@ -2353,12 +2295,7 @@ public abstract class CollectionBinder {
|
|||
return element;
|
||||
}
|
||||
|
||||
private void handleManyToAny(
|
||||
Collection collection,
|
||||
AnnotatedJoinColumns inverseJoinColumns,
|
||||
OnDeleteAction onDeleteAction,
|
||||
XProperty property,
|
||||
MetadataBuildingContext buildingContext) {
|
||||
private void handleManyToAny() {
|
||||
//@ManyToAny
|
||||
//Make sure that collTyp is never used during the @ManyToAny branch: it will be set to void.class
|
||||
final PropertyData inferredData = new PropertyInferredData(
|
||||
|
@ -2414,25 +2351,20 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
|
||||
private void handleOwnedManyToMany(
|
||||
Collection collection,
|
||||
AnnotatedJoinColumns joinColumns,
|
||||
TableBinder associationTableBinder,
|
||||
XProperty property,
|
||||
MetadataBuildingContext context,
|
||||
PersistentClass collectionEntity,
|
||||
boolean isCollectionOfEntities) {
|
||||
//TODO: only for implicit columns?
|
||||
//FIXME NamingStrategy
|
||||
final InFlightMetadataCollector collector = context.getMetadataCollector();
|
||||
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
|
||||
final PersistentClass owner = collection.getOwner();
|
||||
joinColumns.setMappedBy(
|
||||
owner.getEntityName(),
|
||||
collector.getLogicalTableName( owner.getTable() ),
|
||||
collector.getFromMappedBy( owner.getEntityName(), joinColumns.getPropertyName() )
|
||||
);
|
||||
if ( isEmpty( associationTableBinder.getName() ) ) {
|
||||
if ( isEmpty( tableBinder.getName() ) ) {
|
||||
//default value
|
||||
associationTableBinder.setDefaultName(
|
||||
tableBinder.setDefaultName(
|
||||
owner.getClassName(),
|
||||
owner.getEntityName(),
|
||||
owner.getJpaEntityName(),
|
||||
|
@ -2444,15 +2376,26 @@ public abstract class CollectionBinder {
|
|||
joinColumns.getPropertyName()
|
||||
);
|
||||
}
|
||||
associationTableBinder.setJPA2ElementCollection(
|
||||
!isCollectionOfEntities && property.isAnnotationPresent(ElementCollection.class)
|
||||
tableBinder.setJPA2ElementCollection(
|
||||
!isCollectionOfEntities && property.isAnnotationPresent( ElementCollection.class )
|
||||
);
|
||||
collection.setCollectionTable( associationTableBinder.bind() );
|
||||
Table collectionTable = tableBinder.bind();
|
||||
collection.setCollectionTable( collectionTable );
|
||||
handleCheck( collectionTable );
|
||||
}
|
||||
|
||||
private void handleCheck(Table collectionTable) {
|
||||
final Check check = getOverridableAnnotation( property, Check.class, buildingContext );
|
||||
if ( check != null ) {
|
||||
final String name = check.name();
|
||||
final String constraint = check.constraints();
|
||||
collectionTable.addCheck( name.isEmpty()
|
||||
? new CheckConstraint( constraint )
|
||||
: new CheckConstraint( name, constraint ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnownedManyToMany(
|
||||
Collection collection,
|
||||
AnnotatedJoinColumns joinColumns,
|
||||
XClass elementType,
|
||||
PersistentClass collectionEntity,
|
||||
boolean isCollectionOfEntities) {
|
||||
|
@ -2468,7 +2411,7 @@ public abstract class CollectionBinder {
|
|||
try {
|
||||
otherSideProperty = collectionEntity.getRecursiveProperty( mappedBy );
|
||||
}
|
||||
catch (MappingException e) {
|
||||
catch ( MappingException e ) {
|
||||
throw new AnnotationException( "Association '" + safeCollectionRole() +
|
||||
"is 'mappedBy' a property named '" + mappedBy
|
||||
+ "' which does not exist in the target entity '" + elementType.getName() + "'" );
|
||||
|
@ -2484,8 +2427,6 @@ public abstract class CollectionBinder {
|
|||
|
||||
private void detectManyToManyProblems(
|
||||
XClass elementType,
|
||||
XProperty property,
|
||||
PropertyHolder parentPropertyHolder,
|
||||
boolean isCollectionOfEntities,
|
||||
boolean isManyToAny) {
|
||||
|
||||
|
@ -2495,13 +2436,13 @@ public abstract class CollectionBinder {
|
|||
+ "' targets the type '" + elementType.getName() + "' which is not an '@Entity' type" );
|
||||
}
|
||||
else if (isManyToAny) {
|
||||
if ( parentPropertyHolder.getJoinTable( property ) == null ) {
|
||||
if ( propertyHolder.getJoinTable( property ) == null ) {
|
||||
throw new AnnotationException( "Association '" + safeCollectionRole()
|
||||
+ "' is a '@ManyToAny' and must specify a '@JoinTable'" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
final JoinTable joinTableAnn = parentPropertyHolder.getJoinTable( property );
|
||||
final JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
|
||||
if ( joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0 ) {
|
||||
throw new AnnotationException( "Association '" + safeCollectionRole()
|
||||
+ " has a '@JoinTable' with 'inverseJoinColumns' and targets the type '"
|
||||
|
@ -2576,10 +2517,7 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
}
|
||||
|
||||
private void bindCollectionSecondPass(
|
||||
PersistentClass targetEntity,
|
||||
AnnotatedJoinColumns joinColumns,
|
||||
OnDeleteAction onDeleteAction) {
|
||||
private void bindCollectionSecondPass(PersistentClass targetEntity, AnnotatedJoinColumns joinColumns) {
|
||||
|
||||
if ( !isUnownedCollection() ) {
|
||||
createSyntheticPropertyReference(
|
||||
|
@ -2710,8 +2648,8 @@ public abstract class CollectionBinder {
|
|||
this.foreignJoinColumns = annotatedJoinColumns;
|
||||
}
|
||||
|
||||
public void setExplicitAssociationTable(boolean explicitAssocTable) {
|
||||
this.isExplicitAssociationTable = explicitAssocTable;
|
||||
public void setExplicitAssociationTable(boolean isExplicitAssociationTable) {
|
||||
this.isExplicitAssociationTable = isExplicitAssociationTable;
|
||||
}
|
||||
|
||||
public void setElementColumns(AnnotatedColumns elementColumns) {
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.hibernate.annotations.BatchSize;
|
|||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.Check;
|
||||
import org.hibernate.annotations.Checks;
|
||||
import org.hibernate.annotations.DiscriminatorFormula;
|
||||
import org.hibernate.annotations.DiscriminatorOptions;
|
||||
import org.hibernate.annotations.DynamicInsert;
|
||||
|
@ -52,6 +53,7 @@ import org.hibernate.annotations.DynamicUpdate;
|
|||
import org.hibernate.annotations.Filter;
|
||||
import org.hibernate.annotations.Filters;
|
||||
import org.hibernate.annotations.ForeignKey;
|
||||
import org.hibernate.annotations.ForeignKey;
|
||||
import org.hibernate.annotations.Immutable;
|
||||
import org.hibernate.annotations.Loader;
|
||||
import org.hibernate.annotations.NaturalIdCache;
|
||||
|
@ -101,6 +103,7 @@ import org.hibernate.engine.spi.FilterDefinition;
|
|||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.jpa.event.spi.CallbackType;
|
||||
import org.hibernate.mapping.BasicValue;
|
||||
import org.hibernate.mapping.CheckConstraint;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.DependantValue;
|
||||
import org.hibernate.mapping.Join;
|
||||
|
@ -210,6 +213,7 @@ public class EntityBinder {
|
|||
entityBinder.bindEntity();
|
||||
entityBinder.handleClassTable( inheritanceState, superEntity );
|
||||
entityBinder.handleSecondaryTables();
|
||||
entityBinder.handleCheck();
|
||||
final PropertyHolder holder = buildPropertyHolder(
|
||||
clazzToProcess,
|
||||
persistentClass,
|
||||
|
@ -238,6 +242,33 @@ public class EntityBinder {
|
|||
entityBinder.callTypeBinders( persistentClass );
|
||||
}
|
||||
|
||||
private void handleCheck() {
|
||||
if ( annotatedClass.isAnnotationPresent( Checks.class ) ) {
|
||||
// if we have more than one of them they are not overrideable :-/
|
||||
for ( Check check : annotatedClass.getAnnotation( Checks.class ).value() ) {
|
||||
addCheckToEntity( check );
|
||||
}
|
||||
}
|
||||
else {
|
||||
final Check check = getOverridableAnnotation( annotatedClass, Check.class, context );
|
||||
if ( check != null ) {
|
||||
addCheckToEntity( check );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, we store it on the entity.
|
||||
* Later we will come back and figure out which table it belongs to.
|
||||
*/
|
||||
private void addCheckToEntity(Check check) {
|
||||
final String name = check.name();
|
||||
final String constraint = check.constraints();
|
||||
persistentClass.addCheckConstraint( name.isEmpty()
|
||||
? new CheckConstraint( constraint )
|
||||
: new CheckConstraint( name, constraint ) );
|
||||
}
|
||||
|
||||
private void callTypeBinders(PersistentClass persistentClass) {
|
||||
for ( Annotation containingAnnotation : findContainingAnnotations( annotatedClass, TypeBinderType.class ) ) {
|
||||
final TypeBinderType binderType = containingAnnotation.annotationType().getAnnotation( TypeBinderType.class );
|
||||
|
@ -643,14 +674,12 @@ public class EntityBinder {
|
|||
String catalog,
|
||||
List<UniqueConstraintHolder> uniqueConstraints,
|
||||
InFlightMetadataCollector collector) {
|
||||
final Check check = getOverridableAnnotation( annotatedClass, Check.class, context );
|
||||
final RowId rowId = annotatedClass.getAnnotation( RowId.class );
|
||||
bindTable(
|
||||
schema,
|
||||
catalog,
|
||||
table,
|
||||
uniqueConstraints,
|
||||
check == null ? null : check.constraints(),
|
||||
rowId == null ? null : rowId.value(),
|
||||
inheritanceState.hasDenormalizedTable()
|
||||
? collector.getEntityTableXref( superEntity.getEntityName() )
|
||||
|
@ -1662,7 +1691,6 @@ public class EntityBinder {
|
|||
String catalog,
|
||||
String tableName,
|
||||
List<UniqueConstraintHolder> uniqueConstraints,
|
||||
String checkConstraint,
|
||||
String rowId,
|
||||
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
|
||||
|
||||
|
@ -1687,9 +1715,6 @@ public class EntityBinder {
|
|||
);
|
||||
|
||||
table.setRowId( rowId );
|
||||
if ( checkConstraint != null ) {
|
||||
table.addCheckConstraint( checkConstraint );
|
||||
}
|
||||
|
||||
// final Comment comment = annotatedClass.getAnnotation( Comment.class );
|
||||
// if ( comment != null ) {
|
||||
|
|
|
@ -182,19 +182,8 @@ public class OneToOneSecondPass implements SecondPass {
|
|||
manyToOne.markAsLogicalOneToOne();
|
||||
property.setValue( manyToOne );
|
||||
for ( Column column: otherSideJoin.getKey().getColumns() ) {
|
||||
Column copy = new Column();
|
||||
copy.setLength( column.getLength() );
|
||||
copy.setScale( column.getScale() );
|
||||
Column copy = column.clone();
|
||||
copy.setValue( manyToOne );
|
||||
copy.setName( column.getQuotedName() );
|
||||
copy.setNullable( column.isNullable() );
|
||||
copy.setPrecision( column.getPrecision() );
|
||||
copy.setUnique( column.isUnique() );
|
||||
copy.setSqlType( column.getSqlType() );
|
||||
copy.setCheckConstraint( column.getCheckConstraint() );
|
||||
copy.setComment( column.getComment() );
|
||||
copy.setDefaultValue( column.getDefaultValue() );
|
||||
copy.setGeneratedAs( column.getGeneratedAs() );
|
||||
manyToOne.addColumn( copy );
|
||||
}
|
||||
mappedByJoin.addProperty( property );
|
||||
|
@ -275,25 +264,16 @@ public class OneToOneSecondPass implements SecondPass {
|
|||
join.setOptional( true );
|
||||
key.setOnDeleteAction( null );
|
||||
for ( Column column: otherSideProperty.getValue().getColumns() ) {
|
||||
Column copy = new Column();
|
||||
copy.setLength( column.getLength() );
|
||||
copy.setScale( column.getScale() );
|
||||
Column copy = column.clone();
|
||||
copy.setValue( key );
|
||||
copy.setName( column.getQuotedName() );
|
||||
copy.setNullable( column.isNullable() );
|
||||
copy.setPrecision( column.getPrecision() );
|
||||
copy.setUnique( column.isUnique() );
|
||||
copy.setSqlType( column.getSqlType() );
|
||||
copy.setCheckConstraint( column.getCheckConstraint() );
|
||||
copy.setComment( column.getComment() );
|
||||
copy.setDefaultValue( column.getDefaultValue() );
|
||||
column.setGeneratedAs( column.getGeneratedAs() );
|
||||
key.addColumn( copy );
|
||||
}
|
||||
if ( otherSideProperty.getValue() instanceof SortableValue
|
||||
&& !( (SortableValue) otherSideProperty.getValue() ).isSorted() ) {
|
||||
if ( otherSideProperty.getValue() instanceof SortableValue ) {
|
||||
final SortableValue value = (SortableValue) otherSideProperty.getValue();
|
||||
if ( !value.isSorted() ) {
|
||||
key.sortProperties();
|
||||
}
|
||||
}
|
||||
persistentClass.addJoin( join );
|
||||
return join;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class AggregateColumn extends Column {
|
|||
setSqlType( column.getSqlType() );
|
||||
setSqlTypeCode( column.getSqlTypeCode() );
|
||||
uniqueInteger = column.uniqueInteger; //usually useless
|
||||
setCheckConstraint( column.getCheckConstraint() );
|
||||
checkConstraint = column.checkConstraint;
|
||||
setComment( column.getComment() );
|
||||
setDefaultValue( column.getDefaultValue() );
|
||||
setGeneratedAs( column.getGeneratedAs() );
|
||||
|
|
|
@ -334,18 +334,18 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
}
|
||||
|
||||
if ( resolution.getValueConverter() != null ) {
|
||||
column.setSpecializedTypeDeclaration(
|
||||
resolution.getLegacyResolvedBasicType().getSpecializedTypeDeclaration( dialect )
|
||||
);
|
||||
final String declaration = resolution.getLegacyResolvedBasicType().getSpecializedTypeDeclaration(dialect);
|
||||
if ( declaration != null ) {
|
||||
column.setSpecializedTypeDeclaration( declaration );
|
||||
}
|
||||
}
|
||||
|
||||
if ( dialect.supportsColumnCheck() && !column.hasCheckConstraint() ) {
|
||||
column.setCheckConstraint(
|
||||
resolution.getLegacyResolvedBasicType().getCheckCondition(
|
||||
column.getQuotedName( dialect ),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
final String checkCondition = resolution.getLegacyResolvedBasicType()
|
||||
.getCheckCondition( column.getQuotedName( dialect ), dialect );
|
||||
if ( checkCondition != null ) {
|
||||
column.setCheck( new CheckConstraint( checkCondition ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.mapping;
|
||||
|
||||
/**
|
||||
* Represents a table or column level {@code check} constraint.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class CheckConstraint {
|
||||
private String name;
|
||||
private String constraint;
|
||||
|
||||
public CheckConstraint(String name, String constraint) {
|
||||
this.name = name;
|
||||
this.constraint = constraint;
|
||||
}
|
||||
|
||||
public CheckConstraint(String constraint) {
|
||||
this.constraint = constraint;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getConstraint() {
|
||||
return constraint;
|
||||
}
|
||||
|
||||
public void setConstraint(String constraint) {
|
||||
this.constraint = constraint;
|
||||
}
|
||||
|
||||
public String constraintString() {
|
||||
return name == null
|
||||
? " check (" + constraint + ")"
|
||||
: " constraint " + name + " check (" + constraint + ")";
|
||||
}
|
||||
}
|
|
@ -58,7 +58,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
private Integer sqlTypeCode;
|
||||
private boolean quoted;
|
||||
int uniqueInteger;
|
||||
private String checkConstraint;
|
||||
CheckConstraint checkConstraint;
|
||||
private String comment;
|
||||
private String defaultValue;
|
||||
private String generatedAs;
|
||||
|
@ -404,15 +404,15 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
int typeStartIndex = 0;
|
||||
for ( Type subtype : subtypes ) {
|
||||
final int columnSpan = subtype.getColumnSpan(mapping);
|
||||
if (typeStartIndex + columnSpan > typeIndex) {
|
||||
if ( typeStartIndex + columnSpan > typeIndex ) {
|
||||
final int subtypeIndex = typeIndex - typeStartIndex;
|
||||
if (subtype instanceof EntityType) {
|
||||
if ( subtype instanceof EntityType ) {
|
||||
return getTypeForEntityValue(mapping, subtype, subtypeIndex);
|
||||
}
|
||||
if (subtype instanceof ComponentType) {
|
||||
if ( subtype instanceof ComponentType ) {
|
||||
return getTypeForComponentValue(mapping, subtype, subtypeIndex);
|
||||
}
|
||||
if (subtypeIndex == 0) {
|
||||
if ( subtypeIndex == 0 ) {
|
||||
return subtype;
|
||||
}
|
||||
break;
|
||||
|
@ -437,7 +437,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
return getTypeForEntityValue( mapping, entityType.getIdentifierOrUniqueKeyType( mapping ), typeIndex );
|
||||
}
|
||||
else if ( type instanceof ComponentType ) {
|
||||
for (Type subtype : ((ComponentType) type).getSubtypes() ) {
|
||||
for ( Type subtype : ((ComponentType) type).getSubtypes() ) {
|
||||
final Type result = getTypeForEntityValue( mapping, subtype, typeIndex - index );
|
||||
if ( result != null ) {
|
||||
return result;
|
||||
|
@ -490,20 +490,31 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
return specializedTypeDeclaration != null;
|
||||
}
|
||||
|
||||
@Deprecated(since = "6.2")
|
||||
public String getCheckConstraint() {
|
||||
return checkConstraint;
|
||||
return checkConstraint == null ? null : checkConstraint.getConstraint();
|
||||
}
|
||||
|
||||
public void setCheckConstraint(String checkConstraint) {
|
||||
this.checkConstraint = checkConstraint;
|
||||
@Deprecated(since = "6.2")
|
||||
public void setCheckConstraint(String constraint) {
|
||||
checkConstraint = constraint == null ? null : new CheckConstraint( constraint );
|
||||
}
|
||||
|
||||
public void setCheck(CheckConstraint check) {
|
||||
checkConstraint = check;
|
||||
}
|
||||
|
||||
public boolean hasCheckConstraint() {
|
||||
return checkConstraint != null;
|
||||
}
|
||||
|
||||
@Deprecated(since = "6.2")
|
||||
public String checkConstraint() {
|
||||
return checkConstraint == null ? null : " check (" + checkConstraint + ")";
|
||||
return checkConstraint == null ? null : checkConstraint.constraintString();
|
||||
}
|
||||
|
||||
public CheckConstraint getCheck() {
|
||||
return checkConstraint;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -632,24 +643,27 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
@Override
|
||||
public Column clone() {
|
||||
Column copy = new Column();
|
||||
copy.setLength( length );
|
||||
copy.setScale( scale );
|
||||
copy.setValue( value );
|
||||
copy.setTypeIndex( typeIndex );
|
||||
copy.setName( getQuotedName() );
|
||||
copy.setNullable( nullable );
|
||||
copy.setPrecision( precision );
|
||||
copy.setUnique( unique );
|
||||
copy.setSqlType( sqlTypeName );
|
||||
copy.setSqlTypeCode( sqlTypeCode );
|
||||
copy.length = length;
|
||||
copy.precision = precision;
|
||||
copy.scale = scale;
|
||||
copy.value = value;
|
||||
copy.typeIndex = typeIndex;
|
||||
copy.name = name;
|
||||
copy.quoted = quoted;
|
||||
copy.nullable = nullable;
|
||||
copy.unique = unique;
|
||||
copy.sqlTypeName = sqlTypeName;
|
||||
copy.sqlTypeCode = sqlTypeCode;
|
||||
copy.uniqueInteger = uniqueInteger; //usually useless
|
||||
copy.setCheckConstraint( checkConstraint );
|
||||
copy.setComment( comment );
|
||||
copy.setDefaultValue( defaultValue );
|
||||
copy.setGeneratedAs( generatedAs );
|
||||
copy.setAssignmentExpression( assignmentExpression );
|
||||
copy.setCustomRead( customRead );
|
||||
copy.setCustomWrite( customWrite );
|
||||
copy.checkConstraint = checkConstraint;
|
||||
copy.comment = comment;
|
||||
copy.defaultValue = defaultValue;
|
||||
copy.generatedAs = generatedAs;
|
||||
copy.assignmentExpression = assignmentExpression;
|
||||
copy.customRead = customRead;
|
||||
copy.customWrite = customWrite;
|
||||
copy.specializedTypeDeclaration = specializedTypeDeclaration;
|
||||
copy.columnSize = columnSize;
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,15 +48,15 @@ public class Join implements AttributeContainer, Serializable {
|
|||
private ExecuteUpdateResultCheckStyle deleteCheckStyle;
|
||||
|
||||
@Override
|
||||
public void addProperty(Property prop) {
|
||||
properties.add(prop);
|
||||
declaredProperties.add(prop);
|
||||
prop.setPersistentClass( getPersistentClass() );
|
||||
public void addProperty(Property property) {
|
||||
properties.add( property );
|
||||
declaredProperties.add( property );
|
||||
property.setPersistentClass( getPersistentClass() );
|
||||
}
|
||||
|
||||
public void addMappedsuperclassProperty(Property prop) {
|
||||
properties.add(prop);
|
||||
prop.setPersistentClass( getPersistentClass() );
|
||||
public void addMappedsuperclassProperty( Property property ) {
|
||||
properties.add( property );
|
||||
property.setPersistentClass( getPersistentClass() );
|
||||
}
|
||||
|
||||
public List<Property> getDeclaredProperties() {
|
||||
|
@ -72,8 +72,8 @@ public class Join implements AttributeContainer, Serializable {
|
|||
return declaredProperties.iterator();
|
||||
}
|
||||
|
||||
public boolean containsProperty(Property prop) {
|
||||
return properties.contains(prop);
|
||||
public boolean containsProperty(Property property) {
|
||||
return properties.contains( property );
|
||||
}
|
||||
|
||||
@Deprecated(since = "6.0")
|
||||
|
@ -84,6 +84,7 @@ public class Join implements AttributeContainer, Serializable {
|
|||
public Table getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
public void setTable(Table table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
@ -91,6 +92,7 @@ public class Join implements AttributeContainer, Serializable {
|
|||
public KeyValue getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(KeyValue key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
@ -116,11 +118,11 @@ public class Join implements AttributeContainer, Serializable {
|
|||
|
||||
public void createPrimaryKey() {
|
||||
//Primary key constraint
|
||||
PrimaryKey pk = new PrimaryKey( table );
|
||||
pk.setName( PK_ALIAS.toAliasString( table.getName() ) );
|
||||
table.setPrimaryKey(pk);
|
||||
PrimaryKey primaryKey = new PrimaryKey( table );
|
||||
primaryKey.setName( PK_ALIAS.toAliasString( table.getName() ) );
|
||||
table.setPrimaryKey(primaryKey);
|
||||
|
||||
pk.addColumns( getKey() );
|
||||
primaryKey.addColumns( getKey() );
|
||||
}
|
||||
|
||||
public int getPropertySpan() {
|
||||
|
@ -194,9 +196,8 @@ public class Join implements AttributeContainer, Serializable {
|
|||
}
|
||||
|
||||
public boolean isLazy() {
|
||||
Iterator<Property> iter = getPropertyIterator();
|
||||
while ( iter.hasNext() ) {
|
||||
if ( !iter.next().isLazy() ) {
|
||||
for ( Property property : properties ) {
|
||||
if ( !property.isLazy() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.mapping;
|
|||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -26,19 +25,24 @@ import org.hibernate.boot.spi.ClassLoaderAccess;
|
|||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.engine.OptimisticLockStyle;
|
||||
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.FilterConfiguration;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.JoinedIterator;
|
||||
import org.hibernate.internal.util.collections.JoinedList;
|
||||
import org.hibernate.internal.util.collections.SingletonIterator;
|
||||
import org.hibernate.metamodel.MappingMetamodel;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.jpa.event.spi.CallbackDefinition;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.sql.Alias;
|
||||
import org.hibernate.sql.Template;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||
import static org.hibernate.internal.util.StringHelper.root;
|
||||
|
||||
/**
|
||||
|
@ -98,6 +102,8 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
|
|||
private Component identifierMapper;
|
||||
private List<CallbackDefinition> callbackDefinitions;
|
||||
|
||||
private final List<CheckConstraint> checkConstraints = new ArrayList<>();
|
||||
|
||||
// Custom SQL
|
||||
private String customSQLInsert;
|
||||
private boolean customInsertCallable;
|
||||
|
@ -725,9 +731,9 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
|
|||
if ( !prop.isValid( mapping ) ) {
|
||||
final Type type = prop.getType();
|
||||
final int actualColumns = prop.getColumnSpan();
|
||||
final int requiredColumns = type.getColumnSpan(mapping);
|
||||
final int requiredColumns = type.getColumnSpan( mapping );
|
||||
throw new MappingException(
|
||||
"Property '" + StringHelper.qualify( getEntityName(), prop.getName() )
|
||||
"Property '" + qualify( getEntityName(), prop.getName() )
|
||||
+ "' maps to " + actualColumns + " columns but " + requiredColumns
|
||||
+ " columns are required (type '" + type.getName()
|
||||
+ "' spans " + requiredColumns + " columns)"
|
||||
|
@ -1219,7 +1225,52 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
|
|||
// End of @MappedSuperclass support
|
||||
|
||||
public void prepareForMappingModel() {
|
||||
properties.sort( Comparator.comparing( Property::getName ) );
|
||||
properties.sort( comparing( Property::getName ) );
|
||||
}
|
||||
|
||||
public void mappingModelReady(MappingMetamodel mappingMetamodel) {
|
||||
for ( CheckConstraint checkConstraint : checkConstraints ) {
|
||||
final TypeConfiguration typeConfiguration = mappingMetamodel.getTypeConfiguration();
|
||||
final SessionFactoryImplementor sessionFactory = typeConfiguration.getSessionFactory();
|
||||
final List<String> constrainedColumnNames =
|
||||
Template.collectColumnNames( checkConstraint.getConstraint(), typeConfiguration, sessionFactory );
|
||||
final Table primary = getTable();
|
||||
long matches = matchesInTable( constrainedColumnNames, primary );
|
||||
if ( matches == constrainedColumnNames.size() ) {
|
||||
// perfect, all columns matched in the primary table
|
||||
primary.addCheck( checkConstraint );
|
||||
}
|
||||
else {
|
||||
// go searching for a secondary table which better matches
|
||||
Table table = primary;
|
||||
long max = matches;
|
||||
for ( Join join : getJoins() ) {
|
||||
final Table secondary = join.getTable();
|
||||
long secondaryMatches = matchesInTable( constrainedColumnNames, secondary );
|
||||
if ( secondaryMatches > max ) {
|
||||
table = secondary;
|
||||
max = secondaryMatches;
|
||||
}
|
||||
}
|
||||
table.addCheck( checkConstraint );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long matchesInTable(List<String> names, Table table) {
|
||||
return table.getColumns().stream()
|
||||
.filter( col -> col.isQuoted()
|
||||
? names.contains( col.getName() )
|
||||
: names.stream().anyMatch( name -> name.equalsIgnoreCase( col.getName() ) )
|
||||
)
|
||||
.count();
|
||||
}
|
||||
|
||||
public void addCheckConstraint(CheckConstraint checkConstraint) {
|
||||
checkConstraints.add( checkConstraint );
|
||||
}
|
||||
|
||||
public List<CheckConstraint> getCheckConstraints() {
|
||||
return checkConstraints;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.jboss.logging.Logger;
|
|||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static java.util.Collections.unmodifiableMap;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* A mapping model object representing a relational database {@linkplain jakarta.persistence.Table table}.
|
||||
|
@ -64,7 +65,7 @@ public class Table implements Serializable, ContributableDatabaseObject {
|
|||
private final Map<String, Index> indexes = new LinkedHashMap<>();
|
||||
private final Map<String,UniqueKey> uniqueKeys = new LinkedHashMap<>();
|
||||
private int uniqueInteger;
|
||||
private final List<String> checkConstraints = new ArrayList<>();
|
||||
private final List<CheckConstraint> checkConstraints = new ArrayList<>();
|
||||
private String rowId;
|
||||
private String subselect;
|
||||
private boolean isAbstract;
|
||||
|
@ -593,8 +594,13 @@ public class Table implements Serializable, ContributableDatabaseObject {
|
|||
return idValue;
|
||||
}
|
||||
|
||||
@Deprecated(since = "6.2")
|
||||
public void addCheckConstraint(String constraint) {
|
||||
checkConstraints.add( constraint );
|
||||
addCheck( new CheckConstraint( constraint ) );
|
||||
}
|
||||
|
||||
public void addCheck(CheckConstraint check) {
|
||||
checkConstraints.add( check );
|
||||
}
|
||||
|
||||
public boolean containsColumn(Column column) {
|
||||
|
@ -672,7 +678,12 @@ public class Table implements Serializable, ContributableDatabaseObject {
|
|||
return getCheckConstraints().iterator();
|
||||
}
|
||||
|
||||
@Deprecated(since = "6.2")
|
||||
public List<String> getCheckConstraints() {
|
||||
return checkConstraints.stream().map( CheckConstraint::getConstraint ).collect( toList() );
|
||||
}
|
||||
|
||||
public List<CheckConstraint> getChecks() {
|
||||
return unmodifiableList( checkConstraints );
|
||||
}
|
||||
|
||||
|
|
|
@ -252,6 +252,9 @@ public class MappingMetamodelImpl implements MappingMetamodelImplementor, Metamo
|
|||
bootModel.getNamedEntityGraphs().values(),
|
||||
runtimeModelCreationContext
|
||||
);
|
||||
|
||||
bootModel.getEntityBindings().forEach( persistentClass -> persistentClass.mappingModelReady( this ) );
|
||||
|
||||
}
|
||||
|
||||
private void registerEmbeddableMappingType(MetadataImplementor bootModel) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.StringTokenizer;
|
|||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
@ -31,6 +32,8 @@ public final class Template {
|
|||
private static final Set<String> KEYWORDS = new HashSet<>();
|
||||
private static final Set<String> BEFORE_TABLE_KEYWORDS = new HashSet<>();
|
||||
private static final Set<String> FUNCTION_KEYWORDS = new HashSet<>();
|
||||
public static final String PUNCTUATION = "=><!+-*/()',|&`";
|
||||
|
||||
static {
|
||||
KEYWORDS.add("and");
|
||||
KEYWORDS.add("or");
|
||||
|
@ -128,7 +131,7 @@ public final class Template {
|
|||
// identifier references.
|
||||
|
||||
String symbols = new StringBuilder()
|
||||
.append( "=><!+-*/()',|&`" )
|
||||
.append( PUNCTUATION )
|
||||
.append( WHITESPACE )
|
||||
.append( dialect.openQuote() )
|
||||
.append( dialect.closeQuote() )
|
||||
|
@ -358,6 +361,40 @@ public final class Template {
|
|||
return result.toString();
|
||||
}
|
||||
|
||||
public static List<String> collectColumnNames(
|
||||
String checkConstraint,
|
||||
TypeConfiguration typeConfiguration,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final String template = renderWhereStringTemplate(
|
||||
checkConstraint,
|
||||
sessionFactory.getJdbcServices().getDialect(),
|
||||
typeConfiguration,
|
||||
sessionFactory.getQueryEngine().getSqmFunctionRegistry()
|
||||
);
|
||||
final List<String> names = new ArrayList<>();
|
||||
int begin = 0;
|
||||
int match;
|
||||
while ( ( match = template.indexOf(TEMPLATE, begin) ) >= 0 ) {
|
||||
int start = match + TEMPLATE.length() + 1;
|
||||
for ( int loc = start;; loc++ ) {
|
||||
if ( loc == template.length() - 1 ) {
|
||||
names.add( template.substring( start ) );
|
||||
begin = template.length();
|
||||
break;
|
||||
}
|
||||
else {
|
||||
char ch = template.charAt( loc );
|
||||
if ( PUNCTUATION.indexOf(ch) >= 0 || WHITESPACE.indexOf(ch) >= 0 ) {
|
||||
names.add( template.substring( start, loc ) );
|
||||
begin = loc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Takes the where condition provided in the mapping attribute and interpolates the alias.
|
||||
// * Handles sub-selects, quoted identifiers, quoted strings, expressions, SQL functions,
|
||||
|
|
|
@ -101,7 +101,7 @@ class ColumnDefinitions {
|
|||
}
|
||||
|
||||
if ( dialect.supportsColumnCheck() && column.hasCheckConstraint() ) {
|
||||
definition.append( column.checkConstraint() );
|
||||
definition.append( column.getCheck().constraintString() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
|||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.aggregate.AggregateSupport;
|
||||
import org.hibernate.mapping.AggregateColumn;
|
||||
import org.hibernate.mapping.CheckConstraint;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Property;
|
||||
|
@ -151,8 +152,8 @@ public class StandardTableExporter implements Exporter<Table> {
|
|||
|
||||
protected void applyTableCheck(Table table, StringBuilder buf) {
|
||||
if ( dialect.supportsTableCheck() ) {
|
||||
for ( String constraint : table.getCheckConstraints() ) {
|
||||
buf.append( ", check (" ).append( constraint ).append( ')' );
|
||||
for ( CheckConstraint constraint : table.getChecks() ) {
|
||||
buf.append( "," ).append( constraint.constraintString() );
|
||||
}
|
||||
final AggregateSupport aggregateSupport = dialect.getAggregateSupport();
|
||||
if ( aggregateSupport != null && aggregateSupport.supportsComponentCheckConstraints() ) {
|
||||
|
@ -231,7 +232,8 @@ public class StandardTableExporter implements Exporter<Table> {
|
|||
}
|
||||
else {
|
||||
for ( Column subColumn : value.getColumns() ) {
|
||||
final String checkConstraint = subColumn.getCheckConstraint();
|
||||
final CheckConstraint check = subColumn.getCheck();
|
||||
final String checkConstraint = check == null ? null : check.getConstraint();
|
||||
if ( !subColumn.isNullable() || checkConstraint != null ) {
|
||||
final String subColumnName = subColumn.getQuotedName( dialect );
|
||||
final String columnExpression = aggregateSupport.aggregateComponentCustomReadExpression(
|
||||
|
|
Loading…
Reference in New Issue