1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.javagen.agile.db.model;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import javax.xml.bind.annotation.XmlAttribute;
21 import javax.xml.bind.annotation.XmlIDREF;
22 import javax.xml.bind.annotation.XmlRootElement;
23 import javax.xml.bind.annotation.XmlTransient;
24
25 import org.javagen.agile.core.annotation.DefaultValue;
26 import org.javagen.agile.core.model.AbstractModel;
27 import org.javagen.agile.core.model.Model;
28
29 /***
30 * This models a foreign key constraint using one or more ColumnReferences to establish the relationship.
31 * @TODO rename this class to FkConstraint!!
32 *
33 * @author Richard Easterling
34 */
35 @XmlRootElement(name="foreignKeyConstraint")
36 public class FkConstraint extends AbstractModel {
37
38 public static final String DEFAULT_MODEL_TYPE = "FKCOLUMN";
39
40
41 protected FkEnum fkType;
42 protected Table targetTable;
43 protected Cardinality cardinality;
44 protected FkConstraint reverseFkColumn;
45 protected Boolean cascadeUpdate;
46 protected Boolean cascadeDelete;
47 protected Boolean cascadeInsert;
48
49
50
51
52
53
54 public FkConstraint() {
55 super();
56 this.setModelType(DEFAULT_MODEL_TYPE);
57 }
58
59 public FkConstraint(Table parentTable) {
60 this();
61 this.setParentTable(parentTable);
62 }
63
64 /***
65 * Check if foreign key is already managed by a composite primary key,
66 * if so return <code>false</code>.
67 *
68 * @see Hibernate In Action section: Foreign keys in composite primary keys
69 * @param fkConstraint
70 *
71 * TODO verify logic is correct for composite FKs in checkInsertableUpdatable method
72 */
73 public boolean checkInsertableUpdatable() {
74 if (getParentTable().hasCompositeKey())
75 for(ColumnReference ref : this.getColumnReferences()) {
76 if (ref.getLocalColumn().isKey())
77 return false;
78 }
79 return true;
80 }
81
82 /***
83 * Determine if this is a primary key ONE_TO_ONE relationship.
84 */
85 public boolean isPrimaryKeyJoinColumn() {
86 for(ColumnReference ref : this.getColumnReferences()) {
87 if (!(ref.getForeignColumn().isKey() && ref.getLocalColumn().isKey()))
88 return false;
89 }
90 return true;
91 }
92
93 /***
94 * Determine if this relationship can be null.
95 */
96 public boolean getNotNull() {
97 for(ColumnReference ref : this.getColumnReferences()) {
98 if (!ref.getLocalColumn().isKey() && ref.getLocalColumn().getNotNull())
99 return true;
100 }
101 return false;
102 }
103
104 /***
105 * Unique foreign keys restrict relationships to a ONE_TO_ONE cardinality.
106 * @return true if unique foreign key constraint
107 */
108 public boolean unique() {
109 if (getChildModels()!=null && this.getChildModels().size()==1) {
110 FkConstraint fkHolder = FkEnum.IMPORTED.equals(getFkType()) ? this : this.getReverseFkConstraint();
111 Column localColumn = fkHolder.getColumnReferences().get(0).getLocalColumn();
112 return Boolean.TRUE.equals(localColumn.getUnique());
113 } else {
114 return false;
115 }
116 }
117
118 @Override
119 public void copyTo(Model targetModel) {
120 FkConstraint target = (FkConstraint)targetModel;
121 super.copyTo(target);
122 if (this.getFkType()!=null)
123 target.setFkType(this.getFkType());
124 if (this.getCardinality()!=null)
125 target.setCardinality(this.getCardinality());
126 if (this.getCascadeUpdate()!=null)
127 target.setCascadeUpdate(this.getCascadeUpdate());
128 if (this.getCascadeDelete()!=null)
129 target.setCascadeDelete(this.getCascadeDelete());
130 }
131
132 @XmlTransient
133 public Table getParentTable() {
134 return (Table)getParentModel();
135 }
136
137 public void setParentTable(Table parentTable) {
138 this.setParentModel(parentTable);
139 }
140
141 @XmlIDREF
142 public Table getTargetTable() {
143 return targetTable;
144 }
145
146 public void setTargetTable(Table foreignTable) {
147 this.targetTable = foreignTable;
148 }
149 public ColumnReference createColumnReference() {
150 return new ColumnReference(this);
151 }
152
153 public ColumnReference addColumnReference(Column local, Column foreign) {
154 ColumnReference ref = createColumnReference();
155 ref.setForeignColumn(foreign);
156 ref.setLocalColumn(local);
157 this.addColumnReference(ref);
158 local.put(ColumnReference.DEFAULT_MODEL_TYPE, ref);
159 return ref;
160 }
161
162 public void addColumnReference(ColumnReference reference) {
163 super.addChildModel(reference);
164 }
165
166 public void setColumnReferences(List<ColumnReference> references) {
167 List<Model> children = new ArrayList<Model>();
168 children.addAll(references);
169 this.setChildModels(children);
170 }
171
172 @XmlTransient
173 public List<ColumnReference> getColumnReferences() {
174 List<ColumnReference> children = new ArrayList<ColumnReference>();
175 if (getChildModels()!=null) {
176 for(Model child : this.getChildModels()) {
177 children.add( (ColumnReference)child);
178 }
179 }
180 return children;
181 }
182
183 public int columnReferencesSize() {
184 return (getChildModels()!=null) ? getChildModels().size() : 0;
185 }
186
187 public ColumnReference lookupColumnReference(String pkColName, String fkColName) {
188 if (this.getChildModels()==null)
189 return null;
190 for(Model child : getChildModels()) {
191 ColumnReference reference = (ColumnReference)child;
192 if (reference.getLocalColumn().getName().equalsIgnoreCase(pkColName) &&
193 reference.getForeignColumn().getName().equalsIgnoreCase(fkColName))
194 {
195 return reference;
196 }
197 }
198 return null;
199 }
200
201 @XmlAttribute
202 public FkEnum getFkType() {
203 return fkType;
204 }
205
206 public void setFkType(FkEnum fkType) {
207 this.fkType = fkType;
208 }
209
210 /*** Colums that are both PKs and FKs are treated differently (durring updates and inserts for example). */
211 public boolean areLocalColumnsFromPrimaryKey() {
212 boolean result = true;
213 for(Model child : getChildModels()) {
214 ColumnReference ref = (ColumnReference)child;
215 if ( ! ref.getLocalColumn().isKey() )
216 result = false;
217 }
218 return result;
219 }
220
221 @XmlAttribute
222 public Cardinality getCardinality() {
223 return cardinality;
224 }
225
226 public void setCardinality(Cardinality cardinality) {
227 this.cardinality = cardinality;
228 if (reverseFkColumn!=null) {
229 Cardinality revCardinality = cardinality.getReverse();
230 if (reverseFkColumn.cardinality != revCardinality)
231 reverseFkColumn.cardinality = revCardinality;
232 }
233 }
234
235 @XmlTransient
236 public String getCardinalityString() {
237 return cardinality==null ? "null" : cardinality.getMapping();
238 }
239
240 @XmlIDREF
241 public FkConstraint getReverseFkConstraint() {
242 return reverseFkColumn;
243 }
244
245 public void setReverseFkColumn(FkConstraint reverseFkColumn) {
246 this.reverseFkColumn = reverseFkColumn;
247 }
248
249 @XmlAttribute
250 @DefaultValue("false")
251 public Boolean getCascadeDelete() {
252 return cascadeDelete;
253 }
254
255 public void setCascadeDelete(Boolean cascadeDelete) {
256 this.cascadeDelete = cascadeDelete;
257 }
258
259 @XmlAttribute
260 @DefaultValue("false")
261 public Boolean getCascadeUpdate() {
262 return cascadeUpdate;
263 }
264
265 public void setCascadeUpdate(Boolean cascadeUpdate) {
266 this.cascadeUpdate = cascadeUpdate;
267 }
268
269 @XmlAttribute
270 @DefaultValue("false")
271 public Boolean getCascadeInsert() {
272 return cascadeInsert;
273 }
274
275 public void setCascadeInsert(Boolean cascadeInsert) {
276 this.cascadeInsert = cascadeInsert;
277 }
278
279 }