Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion documentation/src/main/asciidoc/introduction/Mapping.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Let's put them in a table, so we can more easily compare the points of differenc
| `SINGLE_TABLE`
| Map every class in the hierarchy to the same table, and uses the value of a _discriminator column_ to determine which concrete class each row represents.
| To retrieve instances of a given class, we only need to query the one table.
| Attributes declared by subclasses map to columns without `NOT NULL` constraints. 💀
| Attributes declared by subclasses map to columns without `NOT NULL` constraints, and so their non-nullability is enforced via a `CHECK` constraint.

Any association may have a `FOREIGN KEY` constraint. 🤓
| Subclass data is denormalized. 🧐
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.CheckConstraint;
import org.hibernate.mapping.Column;
Expand Down Expand Up @@ -98,6 +97,8 @@
import jakarta.persistence.TemporalType;

import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.internal.util.StringHelper.isBlank;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.query.common.TemporalUnit.NANOSECOND;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
import static org.hibernate.type.SqlTypes.*;
Expand Down Expand Up @@ -1234,19 +1235,22 @@ public boolean supportsFromClauseInUpdate() {

@Override
public String getCheckConstraintString(CheckConstraint checkConstraint) {
// The only useful option is 'NOT FOR REPLICATION'
// and it comes before the constraint expression
final String constraintName = checkConstraint.getName();
return constraintName == null
?
" check " + getCheckConstraintOptions( checkConstraint ) + "(" + checkConstraint.getConstraint() + ")"
:
" constraint " + constraintName + " check " + getCheckConstraintOptions( checkConstraint ) + "(" + checkConstraint.getConstraint() + ")";
final String checkWithName =
isBlank( constraintName )
? " check"
: " constraint " + constraintName + " check";
return appendCheckConstraintOptions( checkConstraint, checkWithName )
+ " (" + checkConstraint.getConstraint() + ")";
}

private String getCheckConstraintOptions(CheckConstraint checkConstraint) {
if ( StringHelper.isNotEmpty( checkConstraint.getOptions() ) ) {
return checkConstraint.getOptions() + " ";
}
return "";
@Override
public String appendCheckConstraintOptions(CheckConstraint checkConstraint, String sqlCheckConstraint) {
return isNotEmpty( checkConstraint.getOptions() )
? sqlCheckConstraint + " " + checkConstraint.getOptions()
: sqlCheckConstraint;
}

@Override
Expand Down
4 changes: 3 additions & 1 deletion hibernate-core/src/main/java/org/hibernate/Hibernate.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private Hibernate() {
throw new UnsupportedOperationException();
}

private static final LobHelperImpl lobHelper = new LobHelperImpl();
private static final LobHelper lobHelper = new LobHelperImpl();

/**
* Force initialization of a proxy or persistent collection. In the case of a
Expand Down Expand Up @@ -601,6 +601,8 @@ else if (collectionClass == Collection.class) {
* and {@link java.sql.Clob}.
*
* @return an instance of {@link LobHelper}
*
* @since 7.1
*/
public static LobHelper getLobHelper() {
return lobHelper;
Expand Down
3 changes: 1 addition & 2 deletions hibernate-core/src/main/java/org/hibernate/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -1326,8 +1326,7 @@ public interface Session extends SharedSessionContract, EntityManager {
*
* @return an instance of {@link LobHelper}
*
* @deprecated This method will be removed.
* use {@link Hibernate#getLobHelper()} instead
* @deprecated Use {@link Hibernate#getLobHelper()} instead.
*/
@Deprecated(since="7.0", forRemoval = true)
LobHelper getLobHelper();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ private static boolean applyConstraints(

// Apply Hibernate Validator specific constraints - we cannot import any HV specific classes though!
// No need to check explicitly for @Range. @Range is a composed constraint using @Min and @Max which
// will be taken care later.
// will be taken care of later.
applyLength( property, descriptor, propertyDesc );

// Composing constraints
Expand Down Expand Up @@ -360,7 +360,7 @@ private static boolean isConstraintCompositionOfTypeOr(
return false;
}

final Class<? extends Annotation> composedAnnotation = descriptor.getAnnotation().annotationType();
final var composedAnnotation = descriptor.getAnnotation().annotationType();
return constraintCompositionTypeCache.computeIfAbsent( composedAnnotation, value -> {
for ( Annotation annotation : value.getAnnotations() ) {
if ( "org.hibernate.validator.constraints.ConstraintComposition"
Expand All @@ -383,7 +383,7 @@ private static boolean isConstraintCompositionOfTypeOr(
private static void applyMin(Property property, ConstraintDescriptor<?> descriptor, Dialect dialect) {
if ( Min.class.equals( descriptor.getAnnotation().annotationType() ) ) {
@SuppressWarnings("unchecked")
final ConstraintDescriptor<Min> minConstraint = (ConstraintDescriptor<Min>) descriptor;
final var minConstraint = (ConstraintDescriptor<Min>) descriptor;
final long min = minConstraint.getAnnotation().value();
for ( Selectable selectable : property.getSelectables() ) {
if ( selectable instanceof Column column ) {
Expand All @@ -396,7 +396,7 @@ private static void applyMin(Property property, ConstraintDescriptor<?> descript
private static void applyMax(Property property, ConstraintDescriptor<?> descriptor, Dialect dialect) {
if ( Max.class.equals( descriptor.getAnnotation().annotationType() ) ) {
@SuppressWarnings("unchecked")
final ConstraintDescriptor<Max> maxConstraint = (ConstraintDescriptor<Max>) descriptor;
final var maxConstraint = (ConstraintDescriptor<Max>) descriptor;
final long max = maxConstraint.getAnnotation().value();
for ( Selectable selectable : property.getSelectables() ) {
if ( selectable instanceof Column column ) {
Expand All @@ -420,15 +420,16 @@ private static void applySQLCheck(Column column, String checkConstraint) {
private static boolean isNotNullDescriptor(ConstraintDescriptor<?> descriptor) {
final Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType();
return NotNull.class.equals(annotationType)
|| NotEmpty.class.equals(annotationType)
|| NotBlank.class.equals(annotationType);
|| NotEmpty.class.equals(annotationType)
|| NotBlank.class.equals(annotationType);
}

private static void markNotNull(Property property) {
// single table inheritance should not be forced to null due to shared state
if ( !( property.getPersistentClass() instanceof SingleTableSubclass ) ) {
// composite should not add not-null on all columns
if ( !property.isComposite() ) {
property.setOptional( false );
for ( Selectable selectable : property.getSelectables() ) {
if ( selectable instanceof Column column ) {
column.setNullable( false );
Expand All @@ -449,7 +450,7 @@ private static void markNotNull(Property property) {
private static void applyDigits(Property property, ConstraintDescriptor<?> descriptor) {
if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) {
@SuppressWarnings("unchecked")
final ConstraintDescriptor<Digits> digitsConstraint = (ConstraintDescriptor<Digits>) descriptor;
final var digitsConstraint = (ConstraintDescriptor<Digits>) descriptor;
final int integerDigits = digitsConstraint.getAnnotation().integer();
final int fractionalDigits = digitsConstraint.getAnnotation().fraction();
for ( Selectable selectable : property.getSelectables() ) {
Expand All @@ -466,7 +467,7 @@ private static void applySize(Property property, ConstraintDescriptor<?> descrip
if ( Size.class.equals( descriptor.getAnnotation().annotationType() )
&& String.class.equals( propertyDescriptor.getElementClass() ) ) {
@SuppressWarnings("unchecked")
final ConstraintDescriptor<Size> sizeConstraint = (ConstraintDescriptor<Size>) descriptor;
final var sizeConstraint = (ConstraintDescriptor<Size>) descriptor;
final int max = sizeConstraint.getAnnotation().max();
for ( Column col : property.getColumns() ) {
if ( max < Integer.MAX_VALUE ) {
Expand Down Expand Up @@ -520,19 +521,19 @@ private static Property findPropertyByName(PersistentClass associatedClass, Stri
property = associatedClass.getProperty( element );
}
else {
if ( !property.isComposite() ) {
return null;
if ( property.isComposite() ) {
property = ( (Component) property.getValue() ).getProperty( element );
}
else {
property = ( (Component) property.getValue() ).getProperty( element );
return null;
}
}
}
}
}
catch ( MappingException e ) {
try {
//if we do not find it try to check the identifier mapper
//if we do not find it, try to check the identifier mapper
if ( associatedClass.getIdentifierMapper() == null ) {
return null;
}
Expand All @@ -544,11 +545,11 @@ private static Property findPropertyByName(PersistentClass associatedClass, Stri
property = associatedClass.getIdentifierMapper().getProperty( element );
}
else {
if ( !property.isComposite() ) {
return null;
if ( property.isComposite() ) {
property = ( (Component) property.getValue() ).getProperty( element );
}
else {
property = ( (Component) property.getValue() ).getProperty( element );
return null;
}
}
}
Expand All @@ -562,8 +563,9 @@ private static Property findPropertyByName(PersistentClass associatedClass, Stri
}

private static ValidatorFactory getValidatorFactory(ActivationContext context) {
// IMPL NOTE : We can either be provided a ValidatorFactory or make one. We can be provided
// a ValidatorFactory in 2 different ways. So here we "get" a ValidatorFactory in the following order:
// IMPL NOTE: We can either be provided a ValidatorFactory or make one. We can be provided
// a ValidatorFactory in 2 different ways. So here we "get" a ValidatorFactory
// in the following order:
// 1) Look into SessionFactoryOptions.getValidatorFactoryReference()
// 2) Look into ConfigurationService
// 3) build a new ValidatorFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1757,7 +1757,7 @@ public void processSecondPasses(MetadataBuildingContext buildingContext) {

processFkSecondPassesInOrder();

processSecondPasses(createKeySecondPassList);
processSecondPasses( createKeySecondPassList );
processSecondPasses( secondaryTableSecondPassList );

processSecondPasses( querySecondPassList );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ else if ( columnsAnn != null ) {
else if ( joinColumns == null
&& ( property.hasDirectAnnotationUsage( OneToMany.class )
|| property.hasDirectAnnotationUsage( ElementCollection.class ) ) ) {
OneToMany oneToMany = property.getDirectAnnotationUsage( OneToMany.class );
final OneToMany oneToMany = property.getDirectAnnotationUsage( OneToMany.class );
joinColumns = AnnotatedJoinColumns.buildJoinColumns(
null,
oneToMany == null ? null : nullIfEmpty( oneToMany.mappedBy() ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,11 @@ public static void bindEntityClass(
superEntity.addSubclass( subclass );
}

persistentClass.createConstraints( context );

collector.addEntityBinding( persistentClass );
// process secondary tables and complementary definitions (ie o.h.a.Table)
collector.addSecondPass( new SecondaryTableFromAnnotationSecondPass( entityBinder, holder ) );
collector.addSecondPass( new SecondaryTableSecondPass( entityBinder, holder ) );
collector.addSecondPass( ignored -> persistentClass.createConstraints( context ) );
// comment, checkConstraint, and indexes are processed here
entityBinder.processComplementaryTableDefinitions();
resolveLifecycleCallbacks( clazzToProcess, persistentClass, context.getMetadataCollector() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,7 @@ private AnnotatedColumns bindProperty(
else if ( isManyToOne( property ) ) {
bindManyToOne(
propertyHolder,
nullability,
inferredData,
isIdentifierMapper,
inSecondPass,
Expand All @@ -870,6 +871,7 @@ else if ( isManyToOne( property ) ) {
else if ( isOneToOne( property ) ) {
bindOneToOne(
propertyHolder,
nullability,
inferredData,
isIdentifierMapper,
inSecondPass,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class ToOneBinder {

static void bindManyToOne(
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
boolean isIdentifierMapper,
boolean inSecondPass,
Expand Down Expand Up @@ -102,6 +103,7 @@ && isIdentifier( propertyHolder, propertyBinder, isIdentifierMapper ) ) {
aggregateCascadeTypes( manyToOne.cascade(), hibernateCascade, false, context ),
joinColumns,
propertyHolder,
nullability,
inferredData,
manyToOne.fetch(),
manyToOne.optional(),
Expand Down Expand Up @@ -140,6 +142,7 @@ private static void bindManyToOne(
EnumSet<CascadeType> cascadeStrategy,
AnnotatedJoinColumns joinColumns,
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
FetchType fetchType,
boolean explicitlyOptional,
Expand Down Expand Up @@ -172,7 +175,7 @@ private static void bindManyToOne(
manyToOne.setNotFoundAction( notFoundAction );
manyToOne.setOnDeleteAction( onDeleteAction );
//value.setLazy( fetchMode != FetchMode.JOIN );
if ( !optional ) {
if ( !optional && nullability != Nullability.FORCED_NULL ) {
for ( AnnotatedJoinColumn column : joinColumns.getJoinColumns() ) {
column.setNullable( false );
}
Expand Down Expand Up @@ -413,6 +416,7 @@ else if ( oneToOne != null ) {

static void bindOneToOne(
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
boolean isIdentifierMapper,
boolean inSecondPass,
Expand Down Expand Up @@ -449,6 +453,7 @@ static void bindOneToOne(
oneToOne.optional(),
oneToOne.fetch(),
propertyHolder,
nullability,
inferredData,
nullIfEmpty( oneToOne.mappedBy() ),
trueOneToOne,
Expand All @@ -465,6 +470,7 @@ private static void bindOneToOne(
boolean explicitlyOptional,
FetchType fetchMode,
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
String mappedBy,
boolean trueOneToOne,
Expand Down Expand Up @@ -492,6 +498,7 @@ private static void bindOneToOne(
cascadeStrategy,
joinColumns,
propertyHolder,
nullability,
inferredData,
fetchMode,
explicitlyOptional,
Expand Down
23 changes: 17 additions & 6 deletions hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.HibernateException;
import org.hibernate.Incubating;
import org.hibernate.Internal;
import org.hibernate.Length;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
Expand Down Expand Up @@ -6031,36 +6032,46 @@ public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSuppor
*/
public String getCheckConstraintString(CheckConstraint checkConstraint) {
final String constraintName = checkConstraint.getName();
final String constraint = isBlank( constraintName )
? " check (" + checkConstraint.getConstraint() + ")"
: " constraint " + constraintName + " check (" + checkConstraint.getConstraint() + ")";
final String checkWithName =
isBlank( constraintName )
? " check"
: " constraint " + constraintName + " check";
final String constraint = checkWithName + " (" + checkConstraint.getConstraint() + ")";
return appendCheckConstraintOptions( checkConstraint, constraint );
}

/**
* Append the {@link CheckConstraint} options to SQL check sqlCheckConstraint
* Append the {@linkplain CheckConstraint#getOptions() options} to the given DDL
* string declaring a SQL {@code check} constraint.
*
* @param checkConstraint an instance of {@link CheckConstraint}
* @param sqlCheckConstraint the SQL to append the {@link CheckConstraint} options
*
* @return a SQL expression
*
* @since 7.0
*/
@Internal @Incubating
public String appendCheckConstraintOptions(CheckConstraint checkConstraint, String sqlCheckConstraint) {
return sqlCheckConstraint;
}

/**
* Does this dialect support appending table options SQL fragment at the end of the SQL Table creation statement?
* Does this dialect support appending table options SQL fragment at the end of the SQL table creation statement?
*
* @return {@code true} indicates it does; {@code false} indicates it does not;
*
* @since 7.0
*/
@Deprecated(since = "7.1", forRemoval = true)
public boolean supportsTableOptions() {
return false;
}

/**
* Does this dialect support binding {@link Types#NULL} for {@link PreparedStatement#setNull(int, int)}?
* if it does, then call of {@link PreparedStatement#getParameterMetaData()} could be eliminated for better performance.
* If it does, then the call to {@link PreparedStatement#getParameterMetaData()} may be skipped for
* better performance.
*
* @return {@code true} indicates it does; {@code false} indicates it does not;
* @see org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType
Expand Down
Loading
Loading