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.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 * @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. * * @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()); } /** * Helper method for {@link #generateName(String, Table, Column...)}. * * @return String The generated name */ public static String generateName(String prefix, Table table, List<Column> columns) { //N.B. legacy APIs are involved: can't trust that the columns List is actually //containing Column instances - the generic type isn't consistently enforced. ArrayList<Column> defensive = new ArrayList<>(columns.size()); for (Object o : columns) { if (o instanceof Column) { defensive.add((Column) o); } //else: others might be Formula instances. They don't need to be part of the name generation. } return generateName(prefix, table, defensive.toArray(new Column[0])); } /** * 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); } } } /** * @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; } /** * @return String The prefix to use in generated constraint names. Examples: * "UK_", "FK_", and "PK_". */ public abstract String generatedConstraintNamePrefix(); }