KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > dba > mysql > MySQLAdapter


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. 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,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.dba.mysql;
21
22 import java.sql.Types JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.Comparator JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28
29 import org.apache.cayenne.access.DataNode;
30 import org.apache.cayenne.access.types.ByteArrayType;
31 import org.apache.cayenne.access.types.CharType;
32 import org.apache.cayenne.access.types.ExtendedTypeMap;
33 import org.apache.cayenne.dba.JdbcAdapter;
34 import org.apache.cayenne.dba.PkGenerator;
35 import org.apache.cayenne.map.DbAttribute;
36 import org.apache.cayenne.map.DbEntity;
37 import org.apache.cayenne.map.DbRelationship;
38 import org.apache.cayenne.query.Query;
39 import org.apache.cayenne.query.SQLAction;
40
41 /**
42  * DbAdapter implementation for <a HREF="http://www.mysql.com">MySQL RDBMS</a>.
43  * <h3>Foreign Key Constraint Handling</h3>
44  * <p>
45  * Foreign key constraints are supported by InnoDB engine and NOT supported by MyISAM
46  * engine. This adapter by default assumes MyISAM, so
47  * {@link org.apache.cayenne.dba.JdbcAdapter#supportsFkConstraints()} will return
48  * false. Users can manually change this by calling
49  * <em>setSupportsFkConstraints(true)</em> or better by using an
50  * {@link org.apache.cayenne.dba.AutoAdapter}, i.e. not entering the adapter name at
51  * all for the DataNode, letting Cayenne guess it in runtime. In the later case Cayenne
52  * will check the <em>table_type</em> MySQL variable to detect whether InnoDB is the
53  * default, and configure the adapter accordingly.
54  * <h3>Sample Connection Settings</h3>
55  * <ul>
56  * <li>Adapter name: org.apache.cayenne.dba.mysql.MySQLAdapter</li>
57  * <li>DB URL: jdbc: mysql://serverhostname/dbname</li>
58  * <li>Driver Class: com.mysql.jdbc.Driver</li>
59  * </ul>
60  *
61  * @author Andrus Adamchik
62  */

63 public class MySQLAdapter extends JdbcAdapter {
64
65     public MySQLAdapter() {
66         // init defaults
67
this.setSupportsFkConstraints(false);
68         this.setSupportsUniqueConstraints(true);
69         this.setSupportsGeneratedKeys(true);
70     }
71
72     /**
73      * Uses special action builder to create the right action.
74      *
75      * @since 1.2
76      */

77     public SQLAction getAction(Query query, DataNode node) {
78         return query.createSQLAction(new MySQLActionBuilder(this, node
79                 .getEntityResolver()));
80     }
81
82     public String JavaDoc dropTable(DbEntity entity) {
83         return "DROP TABLE IF EXISTS " + entity.getFullyQualifiedName() + " CASCADE";
84     }
85
86     /**
87      * Installs appropriate ExtendedTypes used as converters for passing values between
88      * JDBC and Java layers.
89      */

90     protected void configureExtendedTypes(ExtendedTypeMap map) {
91         super.configureExtendedTypes(map);
92
93         // must handle CLOBs as strings, otherwise there
94
// are problems with NULL clobs that are treated
95
// as empty strings... somehow this doesn't happen
96
// for BLOBs (ConnectorJ v. 3.0.9)
97
map.registerType(new CharType(false, false));
98         map.registerType(new ByteArrayType(false, false));
99     }
100
101     public DbAttribute buildAttribute(
102             String JavaDoc name,
103             String JavaDoc typeName,
104             int type,
105             int size,
106             int precision,
107             boolean allowNulls) {
108
109         if (typeName != null) {
110             typeName = typeName.toLowerCase();
111         }
112
113         // all LOB types are returned by the driver as OTHER... must remap them manually
114
// (at least on MySQL 3.23)
115
if (type == Types.OTHER) {
116             if ("longblob".equals(typeName)) {
117                 type = Types.BLOB;
118             }
119             else if ("mediumblob".equals(typeName)) {
120                 type = Types.BLOB;
121             }
122             else if ("blob".equals(typeName)) {
123                 type = Types.BLOB;
124             }
125             else if ("tinyblob".equals(typeName)) {
126                 type = Types.VARBINARY;
127             }
128             else if ("longtext".equals(typeName)) {
129                 type = Types.CLOB;
130             }
131             else if ("mediumtext".equals(typeName)) {
132                 type = Types.CLOB;
133             }
134             else if ("text".equals(typeName)) {
135                 type = Types.CLOB;
136             }
137             else if ("tinytext".equals(typeName)) {
138                 type = Types.VARCHAR;
139             }
140         }
141         // types like "int unsigned" map to Long
142
else if (typeName != null && typeName.endsWith(" unsigned")) {
143             // per
144
// http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-type-conversions.html
145
if (typeName.equals("int unsigned")
146                     || typeName.equals("integer unsigned")
147                     || typeName.equals("mediumint unsigned")) {
148                 type = Types.BIGINT;
149             }
150             // BIGINT UNSIGNED maps to BigInteger according to MySQL docs, but there is no
151
// JDBC mapping for BigInteger
152
}
153
154         return super.buildAttribute(name, typeName, type, size, precision, allowNulls);
155     }
156
157     /**
158      * Returns null, since views are not yet supported in MySQL. Views are available on
159      * newer versions of MySQL.
160      */

161     public String JavaDoc tableTypeForView() {
162         return null;
163     }
164
165     /**
166      * Creates and returns a primary key generator. Overrides superclass implementation to
167      * return an instance of MySQLPkGenerator that does the correct table locking.
168      */

169     protected PkGenerator createPkGenerator() {
170         return new MySQLPkGenerator();
171     }
172
173     /**
174      * Overrides super implementation to explicitly set table engine to InnoDB if FK
175      * constraints are supported by this adapter.
176      */

177     public String JavaDoc createTable(DbEntity entity) {
178         String JavaDoc ddlSQL = super.createTable(entity);
179         // force InnoDB tables - by default constraints are enabled
180
ddlSQL += " ENGINE=InnoDB";
181         return ddlSQL;
182     }
183
184     /**
185      * Customizes PK clause semantics to ensure that generated columns are in the
186      * beginning of the PK definition, as this seems to be a requirement for InnoDB
187      * tables.
188      *
189      * @since 1.2
190      */

191     // See CAY-358 for details of the InnoDB problem
192
protected void createTableAppendPKClause(StringBuffer JavaDoc sqlBuffer, DbEntity entity) {
193
194         // must move generated to the front...
195
List JavaDoc pkList = new ArrayList JavaDoc(entity.getPrimaryKey());
196         Collections.sort(pkList, new PKComparator());
197
198         Iterator JavaDoc pkit = pkList.iterator();
199         if (pkit.hasNext()) {
200
201             sqlBuffer.append(", PRIMARY KEY (");
202             boolean firstPk = true;
203             while (pkit.hasNext()) {
204                 if (firstPk)
205                     firstPk = false;
206                 else
207                     sqlBuffer.append(", ");
208
209                 DbAttribute at = (DbAttribute) pkit.next();
210                 sqlBuffer.append(at.getName());
211             }
212             sqlBuffer.append(')');
213         }
214
215         // if FK constraints are supported, we must add indices to all FKs
216
// Note that according to MySQL docs, FK indexes are created automatically when
217
// constraint is defined, starting at MySQL 4.1.2
218
if (supportsFkConstraints()) {
219             Iterator JavaDoc relationships = entity.getRelationships().iterator();
220             while (relationships.hasNext()) {
221                 DbRelationship relationship = (DbRelationship) relationships.next();
222                 if (relationship.getJoins().size() > 0
223                         && relationship.isToPK()
224                         && !relationship.isToDependentPK()) {
225
226                     sqlBuffer.append(", KEY (");
227
228                     Iterator JavaDoc columns = relationship.getSourceAttributes().iterator();
229                     DbAttribute column = (DbAttribute) columns.next();
230                     sqlBuffer.append(column.getName());
231
232                     while (columns.hasNext()) {
233                         column = (DbAttribute) columns.next();
234                         sqlBuffer.append(", ").append(column.getName());
235                     }
236
237                     sqlBuffer.append(")");
238                 }
239             }
240         }
241     }
242
243     /**
244      * Appends AUTO_INCREMENT clause to the column definition for generated columns.
245      */

246     protected void createTableAppendColumn(StringBuffer JavaDoc sqlBuffer, DbAttribute column) {
247         super.createTableAppendColumn(sqlBuffer, column);
248
249         if (column.isGenerated()) {
250             sqlBuffer.append(" AUTO_INCREMENT");
251         }
252     }
253
254     final class PKComparator implements Comparator JavaDoc {
255
256         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
257             DbAttribute a1 = (DbAttribute) o1;
258             DbAttribute a2 = (DbAttribute) o2;
259             if (a1.isGenerated() != a2.isGenerated()) {
260                 return a1.isGenerated() ? -1 : 1;
261             }
262             else {
263                 return a1.getName().compareTo(a2.getName());
264             }
265         }
266     }
267 }
268
Popular Tags