View Javadoc

1   package org.apache.torque.util;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.Serializable;
20  import java.util.List;
21  
22  import org.apache.torque.TorqueException;
23  import org.apache.torque.adapter.DB;
24  import org.apache.torque.map.DatabaseMap;
25  
26  /***
27   * Factored out code that is used to generate Join Code. This code comes
28   * from BasePeer and is put here to reduce complexity in the BasePeer class.
29   * You should not use the methods here directly!
30   *
31   * @author <a href="mailto:fischer@seitenbau.de">Thomas Fischer</a>
32   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
33   * @version $Id: JoinBuilder.java 239636 2005-08-24 12:38:09Z henning $
34   */
35  public abstract class JoinBuilder
36          implements Serializable
37  {
38      /***
39       * adds the Joins from the criteria to the query
40       * @param criteria the criteria from which the Joins are taken
41       * @param query the query to which the Joins should be added
42       * @throws TorqueException if the Joins can not be processed
43       */
44      public static final void processJoins(
45              final DB db,
46              final DatabaseMap dbMap,
47              final Criteria criteria,
48              final Query query)
49              throws TorqueException
50      {
51          List criteriaJoins = criteria.getJoins();
52  
53          if (criteriaJoins == null)
54          {
55              return;
56          }
57  
58          UniqueList queryFromClause = query.getFromClause();
59          UniqueList queryWhereClause = query.getWhereClause();
60  
61          for (int i = 0; i < criteriaJoins.size(); i++)
62          {
63              Criteria.Join join = (Criteria.Join) criteriaJoins.get(i);
64              String leftColumn = join.getLeftColumn();
65              String rightColumn = join.getRightColumn();
66  
67              // check if the column names make sense
68              if (leftColumn.indexOf('.') == -1)
69              {
70                  SQLBuilder.throwMalformedColumnNameException("join", leftColumn);
71              }
72              if (rightColumn.indexOf('.') == -1)
73              {
74                  SQLBuilder.throwMalformedColumnNameException("join", rightColumn);
75              }
76  
77              // get the table names
78              // (and the alias names for them if necessary))
79              // Also check whether a case insensitive comparison is needed
80              int dot = leftColumn.lastIndexOf('.');
81              String leftTableName = leftColumn.substring(0, dot);
82  
83              leftTableName =
84                      SQLBuilder.getTableNameForFromClause(leftTableName, criteria);
85  
86              dot = rightColumn.lastIndexOf('.');
87              String rightTableName = rightColumn.substring(0, dot);
88              String dbTableName
89                      = criteria.getTableForAlias(rightTableName);
90  
91              if (dbTableName == null)
92              {
93                  dbTableName = rightTableName;
94              }
95  
96              String columnName = rightColumn.substring(
97                      dot + 1,
98                      rightColumn.length());
99  
100             boolean ignoreCase = (criteria.isIgnoreCase()
101                     && (dbMap
102                             .getTable(dbTableName)
103                             .getColumn(columnName)
104                             .getType()
105                             instanceof String));
106 
107             rightTableName = SQLBuilder.getTableNameForFromClause(
108                     rightTableName, criteria);
109 
110             // now check the join type and add the join to the
111             // appropriate places in the query
112             SqlEnum joinType  = join.getJoinType();
113 
114             if (joinType == null)
115             {
116                 // Do not treat join as explicit join, but add
117                 // the join condition to the where clauses
118                 if (!SQLBuilder.fromClauseContainsTableName(
119                             queryFromClause,
120                             leftTableName))
121                 {
122                     Query.FromElement fromElement
123                             = new Query.FromElement(
124                                     leftTableName, null, null);
125                     queryFromClause.add(fromElement);
126                 }
127                 if (!SQLBuilder.fromClauseContainsTableName(
128                             queryFromClause,
129                             rightTableName))
130                 {
131                     Query.FromElement fromElement
132                             = new Query.FromElement(
133                                     rightTableName, null, null);
134                     queryFromClause.add(fromElement);
135                 }
136                 queryWhereClause.add(
137                         SqlExpression.buildInnerJoin(
138                                 leftColumn, rightColumn, ignoreCase, db));
139             }
140             else
141             {
142                 // check whether the order of the join must be "reversed"
143                 // This if the case if the fromClause already contains
144                 // rightTableName
145 
146                 if (!SQLBuilder.fromClauseContainsTableName(
147                             queryFromClause,
148                             rightTableName))
149                 {
150                     if (!SQLBuilder.fromClauseContainsTableName(
151                                 queryFromClause,
152                                 leftTableName))
153                     {
154                         Query.FromElement fromElement
155                                 = new Query.FromElement(
156                                         leftTableName, null, null);
157                         queryFromClause.add(fromElement);
158                     }
159 
160                     Query.FromElement fromElement
161                             = new Query.FromElement(
162                                     rightTableName, joinType,
163                                     SqlExpression.buildInnerJoin(
164                                             leftColumn, rightColumn,
165                                             ignoreCase, db));
166                     queryFromClause.add(fromElement);
167                 }
168                 else
169                 {
170                     if (SQLBuilder.fromClauseContainsTableName(
171                                 queryFromClause,
172                                 leftTableName))
173                     {
174                         // We cannot add an explicit join if both tables
175                         // are alredy present in the from clause
176                         throw new TorqueException(
177                                 "Unable to create a " + joinType
178                                 + "because both table names "
179                                 + leftTableName + " and " + rightTableName
180                                 + " are already in use. "
181                                 + "Try to create an(other) alias.");
182                     }
183                     // now add the join in reverse order
184                     // rightTableName must not be added
185                     // because it is already present
186                     Query.FromElement fromElement
187                             = new Query.FromElement(
188                                     leftTableName, reverseJoinType(joinType),
189                                     SqlExpression.buildInnerJoin(
190                                             rightColumn, leftColumn,
191                                             ignoreCase, db));
192                     queryFromClause.add(fromElement);
193                 }
194             }
195         }
196     }
197 
198     /***
199      * returns the reversed Join type, i.e. the join type which would produce
200      * the same result if also the joined tables were exchanged:
201      * Example:<br />
202      * table_a left join table_b <br />
203      * produces the same result as  <br />
204      * table_b right join table_a<br />
205      * So "left join" is the reverse of "right join"
206      * @param joinType the join type to be reversed
207      * @return the reversed join type
208      */
209     private static final SqlEnum reverseJoinType(
210             final SqlEnum joinType)
211     {
212         if (SqlEnum.LEFT_JOIN.equals(joinType))
213         {
214             return SqlEnum.RIGHT_JOIN;
215         }
216         else if (SqlEnum.RIGHT_JOIN.equals(joinType))
217         {
218             return SqlEnum.LEFT_JOIN;
219         }
220         else
221         {
222             return joinType;
223         }
224     }
225 }