1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.mapping;
26
27 import java.util.ArrayList;
28 import java.util.Iterator;
29 import java.util.List;
30
31 import org.hibernate.MappingException;
32 import org.hibernate.dialect.Dialect;
33
34 /**
35 * A foreign key constraint
36 * @author Gavin King
37 */
38 public class ForeignKey extends Constraint {
39
40 private Table referencedTable;
41 private String referencedEntityName;
42 private boolean cascadeDeleteEnabled;
43 private List referencedColumns = new ArrayList();
44
45 public String sqlConstraintString(Dialect dialect, String constraintName, String defaultCatalog, String defaultSchema) {
46 String[] cols = new String[ getColumnSpan() ];
47 String[] refcols = new String[ getColumnSpan() ];
48 int i=0;
49 Iterator refiter = null;
50 if(isReferenceToPrimaryKey() ) {
51 refiter = referencedTable.getPrimaryKey().getColumnIterator();
52 }
53 else {
54 refiter = referencedColumns.iterator();
55 }
56
57 Iterator iter = getColumnIterator();
58 while ( iter.hasNext() ) {
59 cols[i] = ( (Column) iter.next() ).getQuotedName(dialect);
60 refcols[i] = ( (Column) refiter.next() ).getQuotedName(dialect);
61 i++;
62 }
63 String result = dialect.getAddForeignKeyConstraintString(
64 constraintName, cols, referencedTable.getQualifiedName(dialect, defaultCatalog, defaultSchema), refcols, isReferenceToPrimaryKey()
65 );
66 return cascadeDeleteEnabled && dialect.supportsCascadeDelete() ?
67 result + " on delete cascade" :
68 result;
69 }
70
71 public Table getReferencedTable() {
72 return referencedTable;
73 }
74
75 private void appendColumns(StringBuffer buf, Iterator columns) {
76 while( columns.hasNext() ) {
77 Column column = (Column) columns.next();
78 buf.append( column.getName() );
79 if ( columns.hasNext() ) buf.append(",");
80 }
81 }
82
83 public void setReferencedTable(Table referencedTable) throws MappingException {
84 //if( isReferenceToPrimaryKey() ) alignColumns(referencedTable); // TODO: possibly remove to allow more piecemal building of a foreignkey.
85
86 this.referencedTable = referencedTable;
87 }
88
89 /**
90 * Validates that columnspan of the foreignkey and the primarykey is the same.
91 *
92 * Furthermore it aligns the length of the underlying tables columns.
93 * @param referencedTable
94 */
95 public void alignColumns() {
96 if ( isReferenceToPrimaryKey() ) alignColumns(referencedTable);
97 }
98
99 private void alignColumns(Table referencedTable) {
100 if ( referencedTable.getPrimaryKey().getColumnSpan()!=getColumnSpan() ) {
101 StringBuffer sb = new StringBuffer();
102 sb.append("Foreign key (")
103 .append( getName() + ":")
104 .append( getTable().getName() )
105 .append(" [");
106 appendColumns( sb, getColumnIterator() );
107 sb.append("])")
108 .append(") must have same number of columns as the referenced primary key (")
109 .append( referencedTable.getName() )
110 .append(" [");
111 appendColumns( sb, referencedTable.getPrimaryKey().getColumnIterator() );
112 sb.append("])");
113 throw new MappingException( sb.toString() );
114 }
115
116 Iterator fkCols = getColumnIterator();
117 Iterator pkCols = referencedTable.getPrimaryKey().getColumnIterator();
118 while ( pkCols.hasNext() ) {
119 ( (Column) fkCols.next() ).setLength( ( (Column) pkCols.next() ).getLength() );
120 }
121
122 }
123
124 public String getReferencedEntityName() {
125 return referencedEntityName;
126 }
127
128 public void setReferencedEntityName(String referencedEntityName) {
129 this.referencedEntityName = referencedEntityName;
130 }
131
132 public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
133 return "alter table " +
134 getTable().getQualifiedName(dialect, defaultCatalog, defaultSchema) +
135 dialect.getDropForeignKeyString() +
136 getName();
137 }
138
139 public boolean isCascadeDeleteEnabled() {
140 return cascadeDeleteEnabled;
141 }
142
143 public void setCascadeDeleteEnabled(boolean cascadeDeleteEnabled) {
144 this.cascadeDeleteEnabled = cascadeDeleteEnabled;
145 }
146
147 public boolean isPhysicalConstraint() {
148 return referencedTable.isPhysicalTable() &&
149 getTable().isPhysicalTable() &&
150 !referencedTable.hasDenormalizedTables();
151 }
152
153 /** Returns the referenced columns if the foreignkey does not refer to the primary key */
154 public List getReferencedColumns() {
155 return referencedColumns;
156 }
157
158 /** Does this foreignkey reference the primary key of the reference table */
159 public boolean isReferenceToPrimaryKey() {
160 return referencedColumns.isEmpty();
161 }
162
163 public void addReferencedColumns(Iterator referencedColumnsIterator) {
164 while ( referencedColumnsIterator.hasNext() ) {
165 Selectable col = (Selectable) referencedColumnsIterator.next();
166 if ( !col.isFormula() ) addReferencedColumn( (Column) col );
167 }
168 }
169
170 private void addReferencedColumn(Column column) {
171 if ( !referencedColumns.contains(column) ) referencedColumns.add(column);
172 }
173
174 public String toString() {
175 if(!isReferenceToPrimaryKey() ) {
176 StringBuffer result = new StringBuffer(getClass().getName() + '(' + getTable().getName() + getColumns() );
177 result.append( " ref-columns:" + '(' + getReferencedColumns() );
178 result.append( ") as " + getName() );
179 return result.toString();
180 }
181 else {
182 return super.toString();
183 }
184
185 }
186 }