Java tutorial
/* * 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.id; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import org.hibernate.HibernateException; import org.hibernate.dialect.Dialect; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; import org.hibernate.type.CustomType; import org.hibernate.type.Type; /** * Factory and helper methods for {@link IdentifierGenerator} framework. * * @author Gavin King * @author Steve Ebersole */ public final class IdentifierGeneratorHelper { private static final CoreMessageLogger LOG = CoreLogging.messageLogger(IdentifierGeneratorHelper.class); /** * Marker object returned from {@link IdentifierGenerator#generate} to indicate that we should short-circuit any * continued generated id checking. Currently this is only used in the case of the * {@link org.hibernate.id.ForeignGenerator foreign} generator as a way to signal that we should use the associated * entity's id value. */ public static final Serializable SHORT_CIRCUIT_INDICATOR = new Serializable() { @Override public String toString() { return "SHORT_CIRCUIT_INDICATOR"; } }; /** * Marker object returned from {@link IdentifierGenerator#generate} to indicate that the entity's identifier will * be generated as part of the datbase insertion. */ public static final Serializable POST_INSERT_INDICATOR = new Serializable() { @Override public String toString() { return "POST_INSERT_INDICATOR"; } }; /** * Get the generated identifier when using identity columns * * @param rs The result set from which to extract the the generated identity. * @param identifier The name of the identifier column * @param type The expected type mapping for the identity value. * @param dialect The current database dialect. * * @return The generated identity value * * @throws SQLException Can be thrown while accessing the result set * @throws HibernateException Indicates a problem reading back a generated identity value. */ public static Serializable getGeneratedIdentity(ResultSet rs, String identifier, Type type, Dialect dialect) throws SQLException, HibernateException { if (!rs.next()) { throw new HibernateException("The database returned no natively generated identity value"); } final Serializable id = get(rs, identifier, type, dialect); LOG.debugf("Natively generated identity: %s", id); return id; } /** * Extract the value from the result set (which is assumed to already have been positioned to the apopriate row) * and wrp it in the appropriate Java numeric type. * * @param rs The result set from which to extract the value. * @param identifier The name of the identifier column * @param type The expected type of the value. * @param dialect The current database dialect. * * @return The extracted value. * * @throws SQLException Indicates problems access the result set * @throws IdentifierGenerationException Indicates an unknown type. */ public static Serializable get(ResultSet rs, String identifier, Type type, Dialect dialect) throws SQLException, IdentifierGenerationException { if (ResultSetIdentifierConsumer.class.isInstance(type)) { return ((ResultSetIdentifierConsumer) type).consumeIdentifier(rs); } if (CustomType.class.isInstance(type)) { final CustomType customType = (CustomType) type; if (ResultSetIdentifierConsumer.class.isInstance(customType.getUserType())) { return ((ResultSetIdentifierConsumer) customType.getUserType()).consumeIdentifier(rs); } } ResultSetMetaData resultSetMetaData = null; int columnCount = 1; try { resultSetMetaData = rs.getMetaData(); columnCount = resultSetMetaData.getColumnCount(); } catch (Exception e) { //Oracle driver will throw NPE } Class clazz = type.getReturnedClass(); if (columnCount == 1) { if (clazz == Long.class) { return rs.getLong(1); } else if (clazz == Integer.class) { return rs.getInt(1); } else if (clazz == Short.class) { return rs.getShort(1); } else if (clazz == String.class) { return rs.getString(1); } else if (clazz == BigInteger.class) { return rs.getBigDecimal(1).setScale(0, BigDecimal.ROUND_UNNECESSARY).toBigInteger(); } else if (clazz == BigDecimal.class) { return rs.getBigDecimal(1).setScale(0, BigDecimal.ROUND_UNNECESSARY); } else { throw new IdentifierGenerationException( "unrecognized id type : " + type.getName() + " -> " + clazz.getName()); } } else { try { return extractIdentifier(rs, identifier, type, clazz); } catch (SQLException e) { if (StringHelper.isQuoted(identifier, dialect)) { return extractIdentifier(rs, StringHelper.unquote(identifier, dialect), type, clazz); } throw e; } } } private static Serializable extractIdentifier(ResultSet rs, String identifier, Type type, Class clazz) throws SQLException { if (clazz == Long.class) { return rs.getLong(identifier); } else if (clazz == Integer.class) { return rs.getInt(identifier); } else if (clazz == Short.class) { return rs.getShort(identifier); } else if (clazz == String.class) { return rs.getString(identifier); } else if (clazz == BigInteger.class) { return rs.getBigDecimal(identifier).setScale(0, BigDecimal.ROUND_UNNECESSARY).toBigInteger(); } else if (clazz == BigDecimal.class) { return rs.getBigDecimal(identifier).setScale(0, BigDecimal.ROUND_UNNECESSARY); } else { throw new IdentifierGenerationException( "unrecognized id type : " + type.getName() + " -> " + clazz.getName()); } } /** * Wrap the given value in the given Java numeric class. * * @param value The primitive value to wrap. * @param clazz The Java numeric type in which to wrap the value. * * @return The wrapped type. * * @throws IdentifierGenerationException Indicates an unhandled 'clazz'. * @deprecated Use the {@link #getIntegralDataTypeHolder holders} instead. */ @Deprecated public static Number createNumber(long value, Class clazz) throws IdentifierGenerationException { if (clazz == Long.class) { return value; } else if (clazz == Integer.class) { return (int) value; } else if (clazz == Short.class) { return (short) value; } else { throw new IdentifierGenerationException("unrecognized id type : " + clazz.getName()); } } public static IntegralDataTypeHolder getIntegralDataTypeHolder(Class integralType) { if (integralType == Long.class || integralType == Integer.class || integralType == Short.class) { return new BasicHolder(integralType); } else if (integralType == BigInteger.class) { return new BigIntegerHolder(); } else if (integralType == BigDecimal.class) { return new BigDecimalHolder(); } else { throw new IdentifierGenerationException( "Unknown integral data type for ids : " + integralType.getName()); } } public static long extractLong(IntegralDataTypeHolder holder) { if (holder.getClass() == BasicHolder.class) { ((BasicHolder) holder).checkInitialized(); return ((BasicHolder) holder).value; } else if (holder.getClass() == BigIntegerHolder.class) { ((BigIntegerHolder) holder).checkInitialized(); return ((BigIntegerHolder) holder).value.longValue(); } else if (holder.getClass() == BigDecimalHolder.class) { ((BigDecimalHolder) holder).checkInitialized(); return ((BigDecimalHolder) holder).value.longValue(); } throw new IdentifierGenerationException("Unknown IntegralDataTypeHolder impl [" + holder + "]"); } public static BigInteger extractBigInteger(IntegralDataTypeHolder holder) { if (holder.getClass() == BasicHolder.class) { ((BasicHolder) holder).checkInitialized(); return BigInteger.valueOf(((BasicHolder) holder).value); } else if (holder.getClass() == BigIntegerHolder.class) { ((BigIntegerHolder) holder).checkInitialized(); return ((BigIntegerHolder) holder).value; } else if (holder.getClass() == BigDecimalHolder.class) { ((BigDecimalHolder) holder).checkInitialized(); // scale should already be set... return ((BigDecimalHolder) holder).value.toBigInteger(); } throw new IdentifierGenerationException("Unknown IntegralDataTypeHolder impl [" + holder + "]"); } public static BigDecimal extractBigDecimal(IntegralDataTypeHolder holder) { if (holder.getClass() == BasicHolder.class) { ((BasicHolder) holder).checkInitialized(); return BigDecimal.valueOf(((BasicHolder) holder).value); } else if (holder.getClass() == BigIntegerHolder.class) { ((BigIntegerHolder) holder).checkInitialized(); return new BigDecimal(((BigIntegerHolder) holder).value); } else if (holder.getClass() == BigDecimalHolder.class) { ((BigDecimalHolder) holder).checkInitialized(); // scale should already be set... return ((BigDecimalHolder) holder).value; } throw new IdentifierGenerationException("Unknown IntegralDataTypeHolder impl [" + holder + "]"); } public static class BasicHolder implements IntegralDataTypeHolder { private final Class exactType; private long value = Long.MIN_VALUE; public BasicHolder(Class exactType) { this.exactType = exactType; if (exactType != Long.class && exactType != Integer.class && exactType != Short.class) { throw new IdentifierGenerationException("Invalid type for basic integral holder : " + exactType); } } public long getActualLongValue() { return value; } public IntegralDataTypeHolder initialize(long value) { this.value = value; return this; } public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException { long value = resultSet.getLong(1); if (resultSet.wasNull()) { value = defaultValue; } return initialize(value); } public void bind(PreparedStatement preparedStatement, int position) throws SQLException { // TODO : bind it as 'exact type'? Not sure if that gains us anything... LOG.tracef("binding parameter [%s] - [%s]", position, value); preparedStatement.setLong(position, value); } public IntegralDataTypeHolder increment() { checkInitialized(); value++; return this; } private void checkInitialized() { if (value == Long.MIN_VALUE) { throw new IdentifierGenerationException("integral holder was not initialized"); } } public IntegralDataTypeHolder add(long addend) { checkInitialized(); value += addend; return this; } public IntegralDataTypeHolder decrement() { checkInitialized(); value--; return this; } public IntegralDataTypeHolder subtract(long subtrahend) { checkInitialized(); value -= subtrahend; return this; } public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor) { return multiplyBy(extractLong(factor)); } public IntegralDataTypeHolder multiplyBy(long factor) { checkInitialized(); value *= factor; return this; } public boolean eq(IntegralDataTypeHolder other) { return eq(extractLong(other)); } public boolean eq(long value) { checkInitialized(); return this.value == value; } public boolean lt(IntegralDataTypeHolder other) { return lt(extractLong(other)); } public boolean lt(long value) { checkInitialized(); return this.value < value; } public boolean gt(IntegralDataTypeHolder other) { return gt(extractLong(other)); } public boolean gt(long value) { checkInitialized(); return this.value > value; } public IntegralDataTypeHolder copy() { BasicHolder copy = new BasicHolder(exactType); copy.value = value; return copy; } public Number makeValue() { // TODO : should we check for truncation? checkInitialized(); if (exactType == Long.class) { return value; } else if (exactType == Integer.class) { return (int) value; } else { return (short) value; } } public Number makeValueThenIncrement() { final Number result = makeValue(); value++; return result; } public Number makeValueThenAdd(long addend) { final Number result = makeValue(); value += addend; return result; } @Override public String toString() { return "BasicHolder[" + exactType.getName() + "[" + value + "]]"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } BasicHolder that = (BasicHolder) o; return value == that.value; } @Override public int hashCode() { return (int) (value ^ (value >>> 32)); } } public static class BigIntegerHolder implements IntegralDataTypeHolder { private BigInteger value; public IntegralDataTypeHolder initialize(long value) { this.value = BigInteger.valueOf(value); return this; } public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException { final BigDecimal rsValue = resultSet.getBigDecimal(1); if (resultSet.wasNull()) { return initialize(defaultValue); } this.value = rsValue.setScale(0, BigDecimal.ROUND_UNNECESSARY).toBigInteger(); return this; } public void bind(PreparedStatement preparedStatement, int position) throws SQLException { preparedStatement.setBigDecimal(position, new BigDecimal(value)); } public IntegralDataTypeHolder increment() { checkInitialized(); value = value.add(BigInteger.ONE); return this; } private void checkInitialized() { if (value == null) { throw new IdentifierGenerationException("integral holder was not initialized"); } } public IntegralDataTypeHolder add(long increment) { checkInitialized(); value = value.add(BigInteger.valueOf(increment)); return this; } public IntegralDataTypeHolder decrement() { checkInitialized(); value = value.subtract(BigInteger.ONE); return this; } public IntegralDataTypeHolder subtract(long subtrahend) { checkInitialized(); value = value.subtract(BigInteger.valueOf(subtrahend)); return this; } public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor) { checkInitialized(); value = value.multiply(extractBigInteger(factor)); return this; } public IntegralDataTypeHolder multiplyBy(long factor) { checkInitialized(); value = value.multiply(BigInteger.valueOf(factor)); return this; } public boolean eq(IntegralDataTypeHolder other) { checkInitialized(); return value.compareTo(extractBigInteger(other)) == 0; } public boolean eq(long value) { checkInitialized(); return this.value.compareTo(BigInteger.valueOf(value)) == 0; } public boolean lt(IntegralDataTypeHolder other) { checkInitialized(); return value.compareTo(extractBigInteger(other)) < 0; } public boolean lt(long value) { checkInitialized(); return this.value.compareTo(BigInteger.valueOf(value)) < 0; } public boolean gt(IntegralDataTypeHolder other) { checkInitialized(); return value.compareTo(extractBigInteger(other)) > 0; } public boolean gt(long value) { checkInitialized(); return this.value.compareTo(BigInteger.valueOf(value)) > 0; } public IntegralDataTypeHolder copy() { BigIntegerHolder copy = new BigIntegerHolder(); copy.value = value; return copy; } public Number makeValue() { checkInitialized(); return value; } public Number makeValueThenIncrement() { final Number result = makeValue(); value = value.add(BigInteger.ONE); return result; } public Number makeValueThenAdd(long addend) { final Number result = makeValue(); value = value.add(BigInteger.valueOf(addend)); return result; } @Override public String toString() { return "BigIntegerHolder[" + value + "]"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } BigIntegerHolder that = (BigIntegerHolder) o; return this.value == null ? that.value == null : value.equals(that.value); } @Override public int hashCode() { return value != null ? value.hashCode() : 0; } } public static class BigDecimalHolder implements IntegralDataTypeHolder { private BigDecimal value; public IntegralDataTypeHolder initialize(long value) { this.value = BigDecimal.valueOf(value); return this; } public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException { final BigDecimal rsValue = resultSet.getBigDecimal(1); if (resultSet.wasNull()) { return initialize(defaultValue); } this.value = rsValue.setScale(0, BigDecimal.ROUND_UNNECESSARY); return this; } public void bind(PreparedStatement preparedStatement, int position) throws SQLException { preparedStatement.setBigDecimal(position, value); } public IntegralDataTypeHolder increment() { checkInitialized(); value = value.add(BigDecimal.ONE); return this; } private void checkInitialized() { if (value == null) { throw new IdentifierGenerationException("integral holder was not initialized"); } } public IntegralDataTypeHolder add(long increment) { checkInitialized(); value = value.add(BigDecimal.valueOf(increment)); return this; } public IntegralDataTypeHolder decrement() { checkInitialized(); value = value.subtract(BigDecimal.ONE); return this; } public IntegralDataTypeHolder subtract(long subtrahend) { checkInitialized(); value = value.subtract(BigDecimal.valueOf(subtrahend)); return this; } public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor) { checkInitialized(); value = value.multiply(extractBigDecimal(factor)); return this; } public IntegralDataTypeHolder multiplyBy(long factor) { checkInitialized(); value = value.multiply(BigDecimal.valueOf(factor)); return this; } public boolean eq(IntegralDataTypeHolder other) { checkInitialized(); return value.compareTo(extractBigDecimal(other)) == 0; } public boolean eq(long value) { checkInitialized(); return this.value.compareTo(BigDecimal.valueOf(value)) == 0; } public boolean lt(IntegralDataTypeHolder other) { checkInitialized(); return value.compareTo(extractBigDecimal(other)) < 0; } public boolean lt(long value) { checkInitialized(); return this.value.compareTo(BigDecimal.valueOf(value)) < 0; } public boolean gt(IntegralDataTypeHolder other) { checkInitialized(); return value.compareTo(extractBigDecimal(other)) > 0; } public boolean gt(long value) { checkInitialized(); return this.value.compareTo(BigDecimal.valueOf(value)) > 0; } public IntegralDataTypeHolder copy() { BigDecimalHolder copy = new BigDecimalHolder(); copy.value = value; return copy; } public Number makeValue() { checkInitialized(); return value; } public Number makeValueThenIncrement() { final Number result = makeValue(); value = value.add(BigDecimal.ONE); return result; } public Number makeValueThenAdd(long addend) { final Number result = makeValue(); value = value.add(BigDecimal.valueOf(addend)); return result; } @Override public String toString() { return "BigDecimalHolder[" + value + "]"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } BigDecimalHolder that = (BigDecimalHolder) o; return this.value == null ? that.value == null : this.value.equals(that.value); } @Override public int hashCode() { return value != null ? value.hashCode() : 0; } } /** * Disallow instantiation of IdentifierGeneratorHelper. */ private IdentifierGeneratorHelper() { } }