finish off jdoc for DDLTypes

This commit is contained in:
Gavin 2022-11-14 12:18:50 +01:00 committed by Gavin King
parent 867b1146ab
commit 1d5f6b5c13
5 changed files with 122 additions and 25 deletions

View File

@ -24,8 +24,13 @@ import java.sql.Types;
* {@link org.hibernate.type.descriptor.java.JavaType#getRecommendedJdbcType}, * {@link org.hibernate.type.descriptor.java.JavaType#getRecommendedJdbcType},
* or when the {@link org.hibernate.annotations.JdbcTypeCode @JdbcTypeCode} * or when the {@link org.hibernate.annotations.JdbcTypeCode @JdbcTypeCode}
* annotation is used, for example. * annotation is used, for example.
* <p>
* A type code may also be used as a key to obtain a dialect-specific
* {@link org.hibernate.type.descriptor.sql.DdlType} for the purposes of
* generating DDL.
* *
* @see org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry * @see org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry
* @see org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry
* *
* @author Christian Beikov * @author Christian Beikov
*/ */

View File

@ -38,7 +38,7 @@ import org.hibernate.type.spi.TypeConfiguration;
public class ArrayJdbcType implements JdbcType { public class ArrayJdbcType implements JdbcType {
public static final ArrayJdbcType INSTANCE = new ArrayJdbcType( ObjectJdbcType.INSTANCE ); public static final ArrayJdbcType INSTANCE = new ArrayJdbcType( ObjectJdbcType.INSTANCE );
private static final ClassValue<Method> NAME_BINDER = new ClassValue<Method>() { private static final ClassValue<Method> NAME_BINDER = new ClassValue<>() {
@Override @Override
protected Method computeValue(Class<?> type) { protected Method computeValue(Class<?> type) {
try { try {
@ -137,7 +137,7 @@ public class ArrayJdbcType implements JdbcType {
throw new HibernateException( "JDBC driver does not support named parameters for setArray. Use positional.", ex ); throw new HibernateException( "JDBC driver does not support named parameters for setArray. Use positional.", ex );
} }
} }
// Not that it's supposed to have setArray(String,Array) by standard. // Note that it's supposed to have setArray(String,Array) by standard.
// There are numerous missing methods that only have versions for positional parameter, // There are numerous missing methods that only have versions for positional parameter,
// but not named ones. // but not named ones.

View File

@ -7,18 +7,23 @@
package org.hibernate.type.descriptor.sql; package org.hibernate.type.descriptor.sql;
import java.io.Serializable; import java.io.Serializable;
import java.sql.Types;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.Size;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.SqlExpressible; import org.hibernate.metamodel.mapping.SqlExpressible;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
/** /**
* Descriptor for a DDL type. * Descriptor for a DDL column type. An instance of this type abstracts over
* a parameterized family of {@linkplain Dialect dialect-specific} SQL types
* with the same {@linkplain #getSqlTypeCode() type code} but varying length,
* precision, and scale. Usually, the types belonging to the family share a
* single {@linkplain #getRawTypeName() type name} in SQL, but in certain
* cases, most notably, in the case of the MySQL LOB types {@code text} and
* {@code blob}, it's the type name itself which is parameter-dependent.
* *
* @author Christian Beikov * @author Christian Beikov
*/ */
@ -46,20 +51,46 @@ public interface DdlType extends Serializable {
String getTypeNamePattern(); String getTypeNamePattern();
/**
* Return a type with length, precision, and scale specified by the given
* {@linkplain Size size object}.
*/
default String getTypeName(Size size) { default String getTypeName(Size size) {
return getTypeName( size.getLength(), size.getPrecision(), size.getScale() ); return getTypeName( size.getLength(), size.getPrecision(), size.getScale() );
} }
/**
* Return a type with the given length, precision, and scale.
*/
String getTypeName(Long size, Integer precision, Integer scale); String getTypeName(Long size, Integer precision, Integer scale);
/**
* Return the database type corresponding to the given {@link JdbcType}
* that may be used as a target type in casting operations using the SQL
* {@code CAST()} function, using the given {@link JavaType} to help
* determine the appropriate precision and scale. The length is usually
* chosen to be the maximum possible length for the dialect.
*
* @see JavaType#getDefaultSqlScale(Dialect, JdbcType)
* @see JavaType#getDefaultSqlPrecision(Dialect, JdbcType)
* @see Dialect#getMaxVarcharLength()
*
* @return The SQL type name
*/
String getCastTypeName(JdbcType jdbcType, JavaType<?> javaType); String getCastTypeName(JdbcType jdbcType, JavaType<?> javaType);
/** /**
* Get the name of the database type appropriate for casting operations * Return the database type with the given length, precision, and scale,
* (via the CAST() SQL function) for the given {@link SqlExpressible} * if specified, corresponding to the {@link SqlExpressible#getJdbcMapping()
* SQL type. * JdbcMapping} of the given {@link SqlExpressible}, that may be used as a
* target type in casting operations using the SQL {@code CAST()} function.
* *
* @return The database type name * @param type the {@link SqlExpressible}
* @param length the length, or null, if unspecified
* @param precision the precision, or null, if unspecified
* @param scale the scale, or null, if unspecified
*
* @return The SQL type name
*/ */
default String getCastTypeName(SqlExpressible type, Long length, Integer precision, Integer scale) { default String getCastTypeName(SqlExpressible type, Long length, Integer precision, Integer scale) {
return getCastTypeName( return getCastTypeName(
@ -71,5 +102,20 @@ public interface DdlType extends Serializable {
); );
} }
/**
* Return the database type with the given length, precision, and scale,
* if specified, corresponding to the given {@link JdbcType}, that may
* be used as a target type in casting operations using the SQL
* {@code CAST()} function, using the given {@link JavaType} to help
* determine the appropriate precision and scale. The length, if not
* explicitly specified, is usually chosen to be the maximum possible
* length for the dialect.
*
* @see JavaType#getDefaultSqlScale(Dialect, JdbcType)
* @see JavaType#getDefaultSqlPrecision(Dialect, JdbcType)
* @see Dialect#getMaxVarcharLength()
*
* @return The SQL type name
*/
String getCastTypeName(JdbcType jdbcType, JavaType<?> javaType, Long length, Integer precision, Integer scale); String getCastTypeName(JdbcType jdbcType, JavaType<?> javaType, Long length, Integer precision, Integer scale);
} }

View File

@ -23,7 +23,8 @@ import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
/** /**
* Basically a map from SQL type code (int) -> {@link DdlType} * A registry mapping {@link org.hibernate.type.SqlTypes JDBC type codes}
* to instances of the {@link DdlType} interface.
* *
* @author Christian Beikov * @author Christian Beikov
* *
@ -42,10 +43,17 @@ public class DdlTypeRegistry implements Serializable {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// baseline descriptors // baseline descriptors
/**
* Add a mapping from the {@linkplain DdlType#getSqlTypeCode() type code}
* of the given {@link DdlType} to the given {@code DdlType}.
*/
public void addDescriptor(DdlType ddlType) { public void addDescriptor(DdlType ddlType) {
addDescriptor( ddlType.getSqlTypeCode(), ddlType ); addDescriptor( ddlType.getSqlTypeCode(), ddlType );
} }
/**
* Add a mapping from the given type code to the given {@link DdlType}.
*/
public void addDescriptor(int sqlTypeCode, DdlType ddlType) { public void addDescriptor(int sqlTypeCode, DdlType ddlType) {
final DdlType previous = ddlTypes.put( sqlTypeCode, ddlType ); final DdlType previous = ddlTypes.put( sqlTypeCode, ddlType );
if ( previous != null && previous != ddlType ) { if ( previous != null && previous != ddlType ) {
@ -57,16 +65,29 @@ public class DdlTypeRegistry implements Serializable {
addSqlType( ddlType, sqlTypeCode ); addSqlType( ddlType, sqlTypeCode );
} }
/**
* Add a mapping from the {@linkplain DdlType#getSqlTypeCode() type code}
* of the given {@link DdlType} to the given {@code DdlType}, if there
* is no mapping already present for that type code.
*/
public void addDescriptorIfAbsent(DdlType ddlType) { public void addDescriptorIfAbsent(DdlType ddlType) {
addDescriptorIfAbsent( ddlType.getSqlTypeCode(), ddlType ); addDescriptorIfAbsent( ddlType.getSqlTypeCode(), ddlType );
} }
/**
* Add a mapping from the given type code to the given {@link DdlType},
* if there is no mapping already present for the given type code.
*/
public void addDescriptorIfAbsent(int sqlTypeCode, DdlType ddlType) { public void addDescriptorIfAbsent(int sqlTypeCode, DdlType ddlType) {
if ( ddlTypes.putIfAbsent( sqlTypeCode, ddlType ) == null ) { if ( ddlTypes.putIfAbsent( sqlTypeCode, ddlType ) == null ) {
addSqlType( ddlType, sqlTypeCode ); addSqlType( ddlType, sqlTypeCode );
} }
} }
/**
* Add a mapping from the given type code to the raw type name of the
* given {@link DdlType}.
*/
private void addSqlType(DdlType ddlType, int sqlTypeCode) { private void addSqlType(DdlType ddlType, int sqlTypeCode) {
for ( String rawTypeName : ddlType.getRawTypeNames() ) { for ( String rawTypeName : ddlType.getRawTypeNames() ) {
final Integer previousSqlTypeCode = sqlTypes.put( rawTypeName, sqlTypeCode ); final Integer previousSqlTypeCode = sqlTypes.put( rawTypeName, sqlTypeCode );
@ -78,7 +99,8 @@ public class DdlTypeRegistry implements Serializable {
} }
/** /**
* Returns the {@link SqlTypes} type code for the given DDL raw type name, or <code>null</code> if it is unknown. * Returns the {@link SqlTypes} type code for the given DDL raw type name, or
* {@code null} if the type code cannot be determined from the registrations.
*/ */
public Integer getSqlTypeCode(String rawTypeName) { public Integer getSqlTypeCode(String rawTypeName) {
return sqlTypes.get( rawTypeName ); return sqlTypes.get( rawTypeName );
@ -87,11 +109,11 @@ public class DdlTypeRegistry implements Serializable {
/** /**
* Returns the registered {@link DdlType} for the given SQL type code. * Returns the registered {@link DdlType} for the given SQL type code.
* <p> * <p>
* Not that the "long" types {@link Types#LONGVARCHAR}, {@link Types#LONGNVARCHAR} * Note that the "long" types {@link Types#LONGVARCHAR}, {@link Types#LONGNVARCHAR},
* and {@link Types#LONGVARBINARY} are considered synonyms for their * and {@link Types#LONGVARBINARY} are considered synonyms for their non-{@code LONG}
* non-{@code LONG} counterparts, with the only difference being that * counterparts, with the only difference being that a different default length is
* a different default length is used: {@link org.hibernate.Length#LONG} * used by default: {@link org.hibernate.Length#LONG} instead of
* instead of {@link org.hibernate.Length#DEFAULT}. * {@link org.hibernate.Length#DEFAULT}.
* *
*/ */
public DdlType getDescriptor(int sqlTypeCode) { public DdlType getDescriptor(int sqlTypeCode) {
@ -113,6 +135,15 @@ public class DdlTypeRegistry implements Serializable {
return ddlType; return ddlType;
} }
/**
* Get the SQL type name for the specified {@link java.sql.Types JDBC type code},
* filling in the placemarkers {@code $l}, {@code $p}, and {@code $s}
* with the default length, precision, and scale for the given SQL dialect.
*
* @param typeCode the JDBC type code
* @param dialect the dialect which determines the default length, precision, and scale
* @return a SQL column type
*/
public String getTypeName(int typeCode, Dialect dialect) { public String getTypeName(int typeCode, Dialect dialect) {
// explicitly enforce dialect's default precisions // explicitly enforce dialect's default precisions
switch ( typeCode ) { switch ( typeCode ) {
@ -133,6 +164,19 @@ public class DdlTypeRegistry implements Serializable {
} }
} }
/**
* Get the SQL type name for the specified {@link java.sql.Types JDBC type code}
* and size, filling in the placemarkers {@code $l}, {@code $p}, and {@code $s}
* with the length, precision, and scale determined by the given {@linkplain Size
* size object}. The returned type name should be of a SQL type large enough to
* accommodate values of the specified size.
*
* @param typeCode the JDBC type code
* @param size an object which determines the length, precision, and scale
*
* @return the associated type name with the smallest capacity that accommodates
* the given size, if available, and the default type name otherwise
*/
public String getTypeName(int typeCode, Size size) { public String getTypeName(int typeCode, Size size) {
return getTypeName( typeCode, size.getLength(), size.getPrecision(), size.getScale() ); return getTypeName( typeCode, size.getLength(), size.getPrecision(), size.getScale() );
} }
@ -140,15 +184,16 @@ public class DdlTypeRegistry implements Serializable {
/** /**
* Get the SQL type name for the specified {@link java.sql.Types JDBC type code} * Get the SQL type name for the specified {@link java.sql.Types JDBC type code}
* and size, filling in the placemarkers {@code $l}, {@code $p}, and {@code $s} * and size, filling in the placemarkers {@code $l}, {@code $p}, and {@code $s}
* with the given length, precision, and scale. * with the given length, precision, and scale. The returned type name should be
* of a SQL type large enough to accommodate values of the specified size.
* *
* @param typeCode the JDBC type code * @param typeCode the JDBC type code
* @param size the SQL length, if any * @param size the SQL length, if any
* @param precision the SQL precision, if any * @param precision the SQL precision, if any
* @param scale the SQL scale, if any * @param scale the SQL scale, if any
* *
* @return the associated name with smallest capacity >= size, if available and * @return the associated type name with the smallest capacity that accommodates
* the default type name otherwise * the given size, if available, and the default type name otherwise
*/ */
public String getTypeName(int typeCode, Long size, Integer precision, Integer scale) { public String getTypeName(int typeCode, Long size, Integer precision, Integer scale) {
final DdlType descriptor = getDescriptor( typeCode ); final DdlType descriptor = getDescriptor( typeCode );
@ -165,12 +210,13 @@ public class DdlTypeRegistry implements Serializable {
} }
/** /**
* Whether or not the given type name has been registered for this dialect (including both hibernate type names and * Determines if there is a registered {@link DdlType} whose {@linkplain
* custom-registered type names). * DdlType#getRawTypeName() raw type name} matches the given type name,
* taking into account DDL types registered by Hibernate.
* *
* @param typeName the type name. * @param typeName the type name.
* *
* @return true if the given string has been registered either as a hibernate type or as a custom-registered one * @return {@code true} if there is a DDL type with the given raw type name
*/ */
public boolean isTypeNameRegistered(final String typeName) { public boolean isTypeNameRegistered(final String typeName) {
for ( DdlType value : ddlTypes.values() ) { for ( DdlType value : ddlTypes.values() ) {

View File

@ -62,7 +62,7 @@ public class ParameterTest extends BaseEntityManagerFunctionalTestCase {
ParameterExpression<Integer[]> param = em.getCriteriaBuilder().parameter( Integer[].class, "theIntegers" ); ParameterExpression<Integer[]> param = em.getCriteriaBuilder().parameter( Integer[].class, "theIntegers" );
criteria.where( em.getCriteriaBuilder().equal( thePath, param ) ); criteria.where( em.getCriteriaBuilder().equal( thePath, param ) );
TypedQuery<MultiTypedBasicAttributesEntity> query = em.createQuery( criteria ); TypedQuery<MultiTypedBasicAttributesEntity> query = em.createQuery( criteria );
query.setParameter( param, new Integer[] { Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(1) } ); query.setParameter( param, new Integer[] {1, 1, 1} );
assertThat( query.getParameterValue( param.getName() ), instanceOf( Integer[].class ) ); assertThat( query.getParameterValue( param.getName() ), instanceOf( Integer[].class ) );
query.getResultList(); query.getResultList();
em.getTransaction().commit(); em.getTransaction().commit();
@ -85,7 +85,7 @@ public class ParameterTest extends BaseEntityManagerFunctionalTestCase {
); );
TypedQuery<MultiTypedBasicAttributesEntity> query = em.createQuery( criteria ); TypedQuery<MultiTypedBasicAttributesEntity> query = em.createQuery( criteria );
Parameter parameter = query.getParameter( "id" ); Parameter<?> parameter = query.getParameter( "id" );
assertEquals( "id", parameter.getName() ); assertEquals( "id", parameter.getName() );
em.getTransaction().commit(); em.getTransaction().commit();
@ -140,7 +140,7 @@ public class ParameterTest extends BaseEntityManagerFunctionalTestCase {
} }
@Override @Override
public Class[] getAnnotatedClasses() { public Class<?>[] getAnnotatedClasses() {
return new Class[] { MultiTypedBasicAttributesEntity.class }; return new Class[] { MultiTypedBasicAttributesEntity.class };
} }
} }