/*
 * 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;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.hibernate.HibernateException;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.Mapping;

A relational constraint.
Author:Gavin King, Brett Meyer
/** * A relational constraint. * * @author Gavin King * @author Brett Meyer */
public abstract class Constraint implements RelationalModel, Exportable, Serializable { private String name; private final ArrayList<Column> columns = new ArrayList<Column>(); private Table table; public String getName() { return name; } public void setName(String name) { this.name = name; }
If a constraint is not explicitly named, this is called to generate a unique hash using the table and column names. Static so the name can be generated prior to creating the Constraint. They're cached, keyed by name, in multiple locations.
Returns:String The generated name
/** * If a constraint is not explicitly named, this is called to generate * a unique hash using the table and column names. * Static so the name can be generated prior to creating the Constraint. * They're cached, keyed by name, in multiple locations. * * @return String The generated name */
public static String generateName(String prefix, Table table, Column... columns) { // Use a concatenation that guarantees uniqueness, even if identical names // exist between all table and column identifiers. StringBuilder sb = new StringBuilder( "table`" + table.getName() + "`" ); // Ensure a consistent ordering of columns, regardless of the order // they were bound. // Clone the list, as sometimes a set of order-dependent Column // bindings are given. Column[] alphabeticalColumns = columns.clone(); Arrays.sort( alphabeticalColumns, ColumnComparator.INSTANCE ); for ( Column column : alphabeticalColumns ) { String columnName = column == null ? "" : column.getName(); sb.append( "column`" ).append( columnName ).append( "`" ); } return prefix + hashedName( sb.toString() ); }
Returns:String The generated name
/** * Helper method for {@link #generateName(String, Table, Column...)}. * * @return String The generated name */
public static String generateName(String prefix, Table table, List<Column> columns) { return generateName( prefix, table, columns.toArray( new Column[columns.size()] ) ); }
Hash a constraint name using MD5. Convert the MD5 digest to base 35 (full alphanumeric), guaranteeing that the length of the name will always be smaller than the 30 character identifier restriction enforced by a few dialects.
Params:
  • s – The name to be hashed.
Returns:String The hased name.
/** * Hash a constraint name using MD5. Convert the MD5 digest to base 35 * (full alphanumeric), guaranteeing * that the length of the name will always be smaller than the 30 * character identifier restriction enforced by a few dialects. * * @param s * The name to be hashed. * @return String The hased name. */
public static String hashedName(String s) { try { MessageDigest md = MessageDigest.getInstance( "MD5" ); md.reset(); md.update( s.getBytes() ); byte[] digest = md.digest(); BigInteger bigInt = new BigInteger( 1, digest ); // By converting to base 35 (full alphanumeric), we guarantee // that the length of the name will always be smaller than the 30 // character identifier restriction enforced by a few dialects. return bigInt.toString( 35 ); } catch ( NoSuchAlgorithmException e ) { throw new HibernateException( "Unable to generate a hashed Constraint name!", e ); } } private static class ColumnComparator implements Comparator<Column> { public static ColumnComparator INSTANCE = new ColumnComparator(); public int compare(Column col1, Column col2) { return col1.getName().compareTo( col2.getName() ); } } public void addColumn(Column column) { if ( !columns.contains( column ) ) { columns.add( column ); } } public void addColumns(Iterator columnIterator) { while ( columnIterator.hasNext() ) { Selectable col = (Selectable) columnIterator.next(); if ( !col.isFormula() ) { addColumn( (Column) col ); } } }
Returns:true if this constraint already contains a column with same name.
/** * @return true if this constraint already contains a column with same name. */
public boolean containsColumn(Column column) { return columns.contains( column ); } public int getColumnSpan() { return columns.size(); } public Column getColumn(int i) { return columns.get( i ); } //todo duplicated method, remove one public Iterator<Column> getColumnIterator() { return columns.iterator(); } public Iterator<Column> columnIterator() { return columns.iterator(); } public Table getTable() { return table; } public void setTable(Table table) { this.table = table; } public boolean isGenerated(Dialect dialect) { return true; } public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) { if ( isGenerated( dialect ) ) { final String tableName = getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ); return String.format( Locale.ROOT, "%s evictData constraint %s", dialect.getAlterTableString( tableName ), dialect.quote( getName() ) ); } else { return null; } } public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) { if ( isGenerated( dialect ) ) { // Certain dialects (ex: HANA) don't support FKs as expected, but other constraints can still be created. // If that's the case, hasAlterTable() will be true, but getAddForeignKeyConstraintString will return // empty string. Prevent blank "alter table" statements. String constraintString = sqlConstraintString( dialect, getName(), defaultCatalog, defaultSchema ); if ( !StringHelper.isEmpty( constraintString ) ) { final String tableName = getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ); return dialect.getAlterTableString( tableName ) + " " + constraintString; } } return null; } public List<Column> getColumns() { return columns; } public abstract String sqlConstraintString( Dialect d, String constraintName, String defaultCatalog, String defaultSchema); public String toString() { return getClass().getName() + '(' + getTable().getName() + getColumns() + ") as " + name; }
Returns:String The prefix to use in generated constraint names. Examples: "UK_", "FK_", and "PK_".
/** * @return String The prefix to use in generated constraint names. Examples: * "UK_", "FK_", and "PK_". */
public abstract String generatedConstraintNamePrefix(); }