KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > core > MtoNBroker


1 package org.apache.ojb.broker.core;
2
3 /* Copyright 2003-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.util.ArrayList JavaDoc;
19 import java.util.Collection JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.sql.SQLException JavaDoc;
23
24 import org.apache.commons.lang.builder.EqualsBuilder;
25 import org.apache.commons.lang.builder.HashCodeBuilder;
26 import org.apache.commons.lang.builder.ToStringBuilder;
27 import org.apache.commons.lang.ArrayUtils;
28 import org.apache.ojb.broker.MtoNImplementor;
29 import org.apache.ojb.broker.OJBRuntimeException;
30 import org.apache.ojb.broker.PersistenceBrokerException;
31 import org.apache.ojb.broker.PersistenceBrokerSQLException;
32 import org.apache.ojb.broker.accesslayer.ResultSetAndStatement;
33 import org.apache.ojb.broker.core.proxy.ProxyHelper;
34 import org.apache.ojb.broker.metadata.ClassDescriptor;
35 import org.apache.ojb.broker.metadata.CollectionDescriptor;
36 import org.apache.ojb.broker.metadata.DescriptorRepository;
37 import org.apache.ojb.broker.metadata.FieldDescriptor;
38 import org.apache.ojb.broker.metadata.JdbcType;
39 import org.apache.ojb.broker.query.Query;
40 import org.apache.ojb.broker.util.logging.Logger;
41 import org.apache.ojb.broker.util.logging.LoggerFactory;
42
43 /**
44  * Manage all stuff related to non-decomposed M:N association.
45  *
46  * @author <a HREF="mailto:thma@apache.org">Thomas Mahler<a>
47  * @author <a HREF="mailto:leandro@ibnetwork.com.br">Leandro Rodrigo Saad Cruz<a>
48  * @author <a HREF="mailto:mattbaird@yahoo.com">Matthew Baird<a>
49  * @author <a HREF="mailto:jbraeuchi@hotmail.com">Jakob Braeuchi</a>
50  * @author <a HREF="mailto:armin@codeAuLait.de">Armin Waibel</a>
51  * @version $Id: MtoNBroker.java,v 1.10.2.9 2005/12/21 22:25:00 tomdz Exp $
52  */

53 public class MtoNBroker
54 {
55     private Logger log = LoggerFactory.getLogger(MtoNBroker.class);
56
57     private PersistenceBrokerImpl pb;
58     /**
59      * Used to store {@link GenericObject} while transaction running, used as
60      * workaround for m:n insert problem.
61      * TODO: find better solution for m:n handling
62      */

63     private List JavaDoc tempObjects = new ArrayList JavaDoc();
64
65     public MtoNBroker(final PersistenceBrokerImpl broker)
66     {
67         this.pb = broker;
68     }
69
70     public void reset()
71     {
72         tempObjects.clear();
73     }
74
75     /**
76      * Stores new values of a M:N association in a indirection table.
77      *
78      * @param cod The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} for the m:n relation
79      * @param realObject The real object
80      * @param otherObj The referenced object
81      * @param mnKeys The all {@link org.apache.ojb.broker.core.MtoNBroker.Key} matching the real object
82      */

83     public void storeMtoNImplementor(CollectionDescriptor cod, Object JavaDoc realObject, Object JavaDoc otherObj, Collection JavaDoc mnKeys)
84     {
85         ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(realObject.getClass());
86         ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, realObject);
87         String JavaDoc[] pkColumns = cod.getFksToThisClass();
88
89         ClassDescriptor otherCld = pb.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(otherObj));
90         ValueContainer[] otherPkValues = pb.serviceBrokerHelper().getKeyValues(otherCld, otherObj);
91
92         String JavaDoc[] otherPkColumns = cod.getFksToItemClass();
93         String JavaDoc table = cod.getIndirectionTable();
94         MtoNBroker.Key key = new MtoNBroker.Key(otherPkValues);
95
96         if(mnKeys.contains(key))
97         {
98             return;
99         }
100
101         /*
102         fix for OJB-76, composite M & N keys that have some fields common
103         find the "shared" indirection table columns, values and remove these from m- or n- side
104         */

105         for(int i = 0; i < otherPkColumns.length; i++)
106         {
107             int index = ArrayUtils.indexOf(pkColumns, otherPkColumns[i]);
108             if(index != -1)
109             {
110                 // shared indirection table column found, remove this column from one side
111
pkColumns = (String JavaDoc[]) ArrayUtils.remove(pkColumns, index);
112                 // remove duplicate value too
113
pkValues = (ValueContainer[]) ArrayUtils.remove(pkValues, index);
114             }
115         }
116
117         String JavaDoc[] cols = mergeColumns(pkColumns, otherPkColumns);
118         String JavaDoc insertStmt = pb.serviceSqlGenerator().getInsertMNStatement(table, pkColumns, otherPkColumns);
119         ValueContainer[] values = mergeContainer(pkValues, otherPkValues);
120         GenericObject gObj = new GenericObject(table, cols, values);
121         if(! tempObjects.contains(gObj))
122         {
123             pb.serviceJdbcAccess().executeUpdateSQL(insertStmt, cld, pkValues, otherPkValues);
124             tempObjects.add(gObj);
125         }
126     }
127
128     /**
129      * get a Collection of Keys of already existing m:n rows
130      *
131      * @param cod
132      * @param obj
133      * @return Collection of Key
134      */

135     public List JavaDoc getMtoNImplementor(CollectionDescriptor cod, Object JavaDoc obj)
136     {
137         ResultSetAndStatement rs = null;
138         ArrayList JavaDoc result = new ArrayList JavaDoc();
139         ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
140         ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, obj);
141         String JavaDoc[] pkColumns = cod.getFksToThisClass();
142         String JavaDoc[] fkColumns = cod.getFksToItemClass();
143         String JavaDoc table = cod.getIndirectionTable();
144
145         String JavaDoc selectStmt = pb.serviceSqlGenerator().getSelectMNStatement(table, fkColumns, pkColumns);
146
147         ClassDescriptor itemCLD = pb.getDescriptorRepository().getDescriptorFor(cod.getItemClass());
148         Collection JavaDoc extents = pb.getDescriptorRepository().getAllConcreteSubclassDescriptors(itemCLD);
149         if(extents.size() > 0)
150         {
151             itemCLD = (ClassDescriptor) extents.iterator().next();
152         }
153         FieldDescriptor[] itemClassPKFields = itemCLD.getPkFields();
154         if(itemClassPKFields.length != fkColumns.length)
155         {
156             throw new PersistenceBrokerException("All pk fields of the element-class need to" +
157                     " be declared in the indirection table. Element class is "
158                     + itemCLD.getClassNameOfObject() + " with " + itemClassPKFields.length + " pk-fields." +
159                     " Declared 'fk-pointing-to-element-class' elements in collection-descriptor are"
160                     + fkColumns.length);
161         }
162         try
163         {
164             rs = pb.serviceJdbcAccess().executeSQL(selectStmt, cld, pkValues, Query.NOT_SCROLLABLE);
165             while(rs.m_rs.next())
166             {
167                 ValueContainer[] row = new ValueContainer[fkColumns.length];
168                 for(int i = 0; i < row.length; i++)
169                 {
170                     row[i] = new ValueContainer(rs.m_rs.getObject(i + 1), itemClassPKFields[i].getJdbcType());
171                 }
172                 result.add(new MtoNBroker.Key(row));
173             }
174         }
175         catch(PersistenceBrokerException e)
176         {
177             throw e;
178         }
179         catch(SQLException JavaDoc e)
180         {
181             throw new PersistenceBrokerSQLException(e);
182         }
183         finally
184         {
185             if(rs != null) rs.close();
186         }
187         return result;
188     }
189
190     /**
191      * delete all rows from m:n table belonging to obj
192      *
193      * @param cod
194      * @param obj
195      */

196     public void deleteMtoNImplementor(CollectionDescriptor cod, Object JavaDoc obj)
197     {
198         ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
199         ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, obj);
200         String JavaDoc[] pkColumns = cod.getFksToThisClass();
201         String JavaDoc table = cod.getIndirectionTable();
202         String JavaDoc deleteStmt = pb.serviceSqlGenerator().getDeleteMNStatement(table, pkColumns, null);
203         pb.serviceJdbcAccess().executeUpdateSQL(deleteStmt, cld, pkValues, null);
204     }
205
206     /**
207      * deletes all rows from m:n table that are not used in relatedObjects
208      *
209      * @param cod
210      * @param obj
211      * @param collectionIterator
212      * @param mnKeys
213      */

214     public void deleteMtoNImplementor(CollectionDescriptor cod, Object JavaDoc obj, Iterator JavaDoc collectionIterator, Collection JavaDoc mnKeys)
215     {
216         if(mnKeys.isEmpty() || collectionIterator == null)
217         {
218             return;
219         }
220         List JavaDoc workList = new ArrayList JavaDoc(mnKeys);
221         MtoNBroker.Key relatedObjKeys;
222         ClassDescriptor relatedCld = pb.getDescriptorRepository().getDescriptorFor(cod.getItemClass());
223         Object JavaDoc relatedObj;
224
225         // remove keys of relatedObject from the existing m:n rows in workList
226
while(collectionIterator.hasNext())
227         {
228             relatedObj = collectionIterator.next();
229             relatedObjKeys = new MtoNBroker.Key(pb.serviceBrokerHelper().getKeyValues(relatedCld, relatedObj, true));
230             workList.remove(relatedObjKeys);
231         }
232
233         // delete all remaining keys in workList
234
ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
235         ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, obj);
236
237         String JavaDoc[] pkColumns = cod.getFksToThisClass();
238         String JavaDoc[] fkColumns = cod.getFksToItemClass();
239         String JavaDoc table = cod.getIndirectionTable();
240         String JavaDoc deleteStmt;
241
242         ValueContainer[] fkValues;
243         Iterator JavaDoc iter = workList.iterator();
244         while(iter.hasNext())
245         {
246             fkValues = ((MtoNBroker.Key) iter.next()).m_containers;
247             deleteStmt = pb.serviceSqlGenerator().getDeleteMNStatement(table, pkColumns, fkColumns);
248             pb.serviceJdbcAccess().executeUpdateSQL(deleteStmt, cld, pkValues, fkValues);
249         }
250     }
251
252     /**
253      * @param m2n
254      */

255     public void storeMtoNImplementor(MtoNImplementor m2n)
256     {
257         if(log.isDebugEnabled()) log.debug("Storing M2N implementor [" + m2n + "]");
258         insertOrDeleteMtoNImplementor(m2n, true);
259     }
260
261     /**
262      * @param m2n
263      */

264     public void deleteMtoNImplementor(MtoNImplementor m2n)
265     {
266         if(log.isDebugEnabled()) log.debug("Deleting M2N implementor [" + m2n + "]");
267         insertOrDeleteMtoNImplementor(m2n, false);
268     }
269
270
271     /**
272      * @see org.apache.ojb.broker.PersistenceBroker#deleteMtoNImplementor
273      */

274     private void insertOrDeleteMtoNImplementor(MtoNImplementor m2nImpl, boolean insert)
275             throws PersistenceBrokerException
276     {
277         //look for a collection descriptor on left such as left.element-class-ref='right'
278
DescriptorRepository dr = pb.getDescriptorRepository();
279
280         Object JavaDoc leftObject = m2nImpl.getLeftObject();
281         Class JavaDoc leftClass = m2nImpl.getLeftClass();
282         Object JavaDoc rightObject = m2nImpl.getRightObject();
283         Class JavaDoc rightClass = m2nImpl.getRightClass();
284
285         //are written per class, maybe referencing abstract classes or interfaces
286
//so let's look for collection descriptors on the left class and try to
287
// handle extents on teh right class
288
ClassDescriptor leftCld = dr.getDescriptorFor(leftClass);
289         ClassDescriptor rightCld = dr.getDescriptorFor(rightClass);
290         //Vector leftColds = leftCld.getCollectionDescriptors();
291
CollectionDescriptor wanted = m2nImpl.getLeftDescriptor();
292
293         if(leftObject == null || rightObject == null)
294         {
295             //TODO: to be implemented, must change MtoNImplementor
296
//deleteMtoNImplementor(wanted,leftObject) || deleteMtoNImplementor(wanted,rightObject)
297
log.error("Can't handle MtoNImplementor in correct way, found a 'null' object");
298         }
299         else
300         {
301             //delete only one row
302
ValueContainer[] leftPkValues = pb.serviceBrokerHelper().getKeyValues(leftCld, leftObject);
303             ValueContainer[] rightPkValues = pb.serviceBrokerHelper().getKeyValues(rightCld, rightObject);
304             String JavaDoc[] pkLeftColumns = wanted.getFksToThisClass();
305             String JavaDoc[] pkRightColumns = wanted.getFksToItemClass();
306             String JavaDoc table = wanted.getIndirectionTable();
307             if(table == null) throw new PersistenceBrokerException("Can't remove MtoN implementor without an indirection table");
308
309             String JavaDoc stmt;
310             String JavaDoc[] cols = mergeColumns(pkLeftColumns, pkRightColumns);
311             ValueContainer[] values = mergeContainer(leftPkValues, rightPkValues);
312             if(insert)
313             {
314                 stmt = pb.serviceSqlGenerator().getInsertMNStatement(table, pkLeftColumns, pkRightColumns);
315                 GenericObject gObj = new GenericObject(table, cols, values);
316                 if(!tempObjects.contains(gObj))
317                 {
318                     pb.serviceJdbcAccess().executeUpdateSQL(stmt, leftCld, leftPkValues, rightPkValues);
319                     tempObjects.add(gObj);
320                 }
321             }
322             else
323             {
324                 stmt = pb.serviceSqlGenerator().getDeleteMNStatement(table, pkLeftColumns, pkRightColumns);
325                 pb.serviceJdbcAccess().executeUpdateSQL(stmt, leftCld, leftPkValues, rightPkValues);
326             }
327         }
328     }
329
330     private String JavaDoc[] mergeColumns(String JavaDoc[] first, String JavaDoc[] second)
331     {
332         String JavaDoc[] cols = new String JavaDoc[first.length + second.length];
333         System.arraycopy(first, 0, cols, 0, first.length);
334         System.arraycopy(second, 0, cols, first.length, second.length);
335         return cols;
336     }
337
338     private ValueContainer[] mergeContainer(ValueContainer[] first, ValueContainer[] second)
339     {
340         ValueContainer[] values = new ValueContainer[first.length + second.length];
341         System.arraycopy(first, 0, values, 0, first.length);
342         System.arraycopy(second, 0, values, first.length, second.length);
343         return values;
344     }
345
346
347
348 // ************************************************************************
349
// inner class
350
// ************************************************************************
351

352     /**
353      * This is a helper class to model a Key of an Object
354      */

355     private static final class Key
356     {
357         final ValueContainer[] m_containers;
358
359         Key(final ValueContainer[] containers)
360         {
361             m_containers = new ValueContainer[containers.length];
362
363             for(int i = 0; i < containers.length; i++)
364             {
365                 Object JavaDoc value = containers[i].getValue();
366                 JdbcType type = containers[i].getJdbcType();
367
368                 // BRJ:
369
// convert all Numbers to Long to simplify equals
370
// Long(100) is not equal to Integer(100)
371
//
372
// could lead to problems when Floats are used as key
373
// converting to String could be a better alternative
374
if(value instanceof Number JavaDoc)
375                 {
376                     value = new Long JavaDoc(((Number JavaDoc) value).longValue());
377                 }
378
379                 m_containers[i] = new ValueContainer(value, type);
380             }
381         }
382
383         public boolean equals(Object JavaDoc other)
384         {
385             if(other == this)
386             {
387                 return true;
388             }
389             if(!(other instanceof Key))
390             {
391                 return false;
392             }
393
394             Key otherKey = (Key) other;
395             EqualsBuilder eb = new EqualsBuilder();
396
397             eb.append(m_containers, otherKey.m_containers);
398             return eb.isEquals();
399         }
400
401         public int hashCode()
402         {
403             HashCodeBuilder hb = new HashCodeBuilder();
404             hb.append(m_containers);
405
406             return hb.toHashCode();
407         }
408     }
409
410
411
412     // ************************************************************************
413
// inner class
414
// ************************************************************************
415
private static final class GenericObject
416     {
417          private String JavaDoc tablename;
418         private String JavaDoc[] columnNames;
419         private ValueContainer[] values;
420
421         public GenericObject(String JavaDoc tablename, String JavaDoc[] columnNames, ValueContainer[] values)
422         {
423             this.tablename = tablename;
424             this.columnNames = columnNames;
425             this.values = values;
426             if(values != null && columnNames.length != values.length)
427             {
428                 throw new OJBRuntimeException("Column name array and value array have NOT same length");
429             }
430         }
431
432         public boolean equals(Object JavaDoc obj)
433         {
434             if(this == obj)
435             {
436                 return true;
437             }
438             boolean result = false;
439             if(obj instanceof GenericObject)
440             {
441                 GenericObject other = (GenericObject) obj;
442                 result = (tablename.equalsIgnoreCase(other.tablename)
443                         && (columnNames != null)
444                         && (other.columnNames != null)
445                         && (columnNames.length == other.columnNames.length));
446
447                 if(result)
448                 {
449                     for (int i = 0; i < columnNames.length; i++)
450                     {
451                         int otherIndex = other.indexForColumn(columnNames[i]);
452                         if(otherIndex < 0)
453                         {
454                             result = false;
455                             break;
456                         }
457                         result = values[i].equals(other.values[otherIndex]);
458                         if(!result) break;
459                     }
460                 }
461             }
462             return result;
463         }
464
465         int indexForColumn(String JavaDoc name)
466         {
467             int result = -1;
468             for (int i = 0; i < columnNames.length; i++)
469             {
470                 if(columnNames[i].equals(name))
471                 {
472                     result = i;
473                     break;
474                 }
475             }
476             return result;
477         }
478
479         public int hashCode()
480         {
481             return super.hashCode();
482         }
483
484         public ValueContainer getValueFor(String JavaDoc columnName)
485         {
486             try
487             {
488                 return values[indexForColumn(columnName)];
489             }
490             catch(Exception JavaDoc e)
491             {
492                 throw new OJBRuntimeException("Can't find value for column " + columnName
493                         + (indexForColumn(columnName) < 0 ? ". Column name was not found" : ""), e);
494             }
495         }
496
497         public String JavaDoc getTablename()
498         {
499             return tablename;
500         }
501
502         public String JavaDoc[] getColumnNames()
503         {
504             return columnNames;
505         }
506
507         public ValueContainer[] getValues()
508         {
509             return values;
510         }
511
512         public void setValues(ValueContainer[] values)
513         {
514             this.values = values;
515         }
516
517         public String JavaDoc toString()
518         {
519             return new ToStringBuilder(this)
520                     .append("tableName", tablename)
521                     .append("columnNames", columnNames)
522                     .append("values", values)
523                     .toString();
524         }
525     }
526 }
527
Popular Tags