You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DbInspector.java 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * Copyright 2004-2011 H2 Group.
  3. * Copyright 2011 James Moger.
  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. package com.iciql;
  18. import com.iciql.Iciql.IQTable;
  19. import com.iciql.util.JdbcUtils;
  20. import com.iciql.util.StringUtils;
  21. import com.iciql.util.Utils;
  22. import java.sql.DatabaseMetaData;
  23. import java.sql.ResultSet;
  24. import java.sql.SQLException;
  25. import java.text.MessageFormat;
  26. import java.util.ArrayList;
  27. import java.util.List;
  28. /**
  29. * Class to inspect a model and a database for the purposes of model validation
  30. * and automatic model generation. This class finds the available schemas and
  31. * tables and serves as the entry point for model generation and validation.
  32. */
  33. public class DbInspector {
  34. private Db db;
  35. private DatabaseMetaData metaData;
  36. private Class<? extends java.util.Date> dateTimeClass = java.util.Date.class;
  37. public DbInspector(Db db) {
  38. this.db = db;
  39. setPreferredDateTimeClass(db.getDialect().getDateTimeClass());
  40. }
  41. /**
  42. * Set the preferred class to store date and time. Possible values are:
  43. * java.util.Date (default) and java.sql.Timestamp.
  44. *
  45. * @param dateTimeClass the new class
  46. */
  47. public void setPreferredDateTimeClass(Class<? extends java.util.Date> dateTimeClass) {
  48. this.dateTimeClass = dateTimeClass;
  49. }
  50. /**
  51. * Generates models class skeletons for schemas and tables. If the table
  52. * name is undefined, models will be generated for every table within the
  53. * specified schema. Additionally, if no schema is defined, models will be
  54. * generated for all schemas and all tables.
  55. *
  56. * @param schema the schema name (optional)
  57. * @param table the table name (optional)
  58. * @param packageName the package name (optional)
  59. * @param annotateSchema (includes schema name in annotation)
  60. * @param trimStrings (trims strings to maxLength of column)
  61. * @return a list of complete model classes as strings, each element a class
  62. */
  63. public List<String> generateModel(String schema, String table, String packageName,
  64. boolean annotateSchema, boolean trimStrings) {
  65. try {
  66. List<String> models = Utils.newArrayList();
  67. List<TableInspector> tables = getTables(schema, table);
  68. for (TableInspector t : tables) {
  69. t.read(metaData);
  70. String model = t.generateModel(packageName, annotateSchema, trimStrings);
  71. models.add(model);
  72. }
  73. return models;
  74. } catch (SQLException s) {
  75. throw new IciqlException(s);
  76. }
  77. }
  78. /**
  79. * Validates a model.
  80. *
  81. * @param model an instance of the model class
  82. * @param throwOnError if errors should cause validation to fail
  83. * @return a list of validation remarks
  84. */
  85. public <T> List<ValidationRemark> validateModel(T model, boolean throwOnError) {
  86. try {
  87. TableInspector inspector = getTable(model);
  88. inspector.read(metaData);
  89. @SuppressWarnings("unchecked")
  90. Class<T> clazz = (Class<T>) model.getClass();
  91. TableDefinition<T> def = db.define(clazz);
  92. return inspector.validate(def, throwOnError);
  93. } catch (SQLException s) {
  94. throw new IciqlException(s);
  95. }
  96. }
  97. private DatabaseMetaData getMetaData() throws SQLException {
  98. if (metaData == null) {
  99. metaData = db.getConnection().getMetaData();
  100. }
  101. return metaData;
  102. }
  103. /**
  104. * Get the table in the database based on the model definition.
  105. *
  106. * @param model an instance of the model class
  107. * @return the table inspector
  108. */
  109. private <T> TableInspector getTable(T model) throws SQLException {
  110. @SuppressWarnings("unchecked")
  111. Class<T> clazz = (Class<T>) model.getClass();
  112. TableDefinition<T> def = db.define(clazz);
  113. boolean forceUpperCase = getMetaData().storesUpperCaseIdentifiers();
  114. String schema = (forceUpperCase && def.schemaName != null) ? def.schemaName.toUpperCase()
  115. : def.schemaName;
  116. String table = forceUpperCase ? def.tableName.toUpperCase() : def.tableName;
  117. List<TableInspector> tables = getTables(schema, table);
  118. return tables.get(0);
  119. }
  120. /**
  121. * Returns a list of tables. This method always returns at least one
  122. * element. If no table is found, an exception is thrown.
  123. *
  124. * @param schema the schema name
  125. * @param table the table name
  126. * @return a list of table inspectors (always contains at least one element)
  127. */
  128. private List<TableInspector> getTables(String schema, String table) throws SQLException {
  129. ResultSet rs = null;
  130. try {
  131. rs = getMetaData().getSchemas();
  132. ArrayList<String> schemaList = Utils.newArrayList();
  133. while (rs.next()) {
  134. schemaList.add(rs.getString("TABLE_SCHEM"));
  135. }
  136. JdbcUtils.closeSilently(rs);
  137. String iciqlTables = DbVersion.class.getAnnotation(IQTable.class).name();
  138. List<TableInspector> tables = Utils.newArrayList();
  139. if (schemaList.size() == 0) {
  140. schemaList.add(null);
  141. }
  142. for (String s : schemaList) {
  143. rs = getMetaData().getTables(null, s, null, new String[]{"TABLE"});
  144. while (rs.next()) {
  145. String t = rs.getString("TABLE_NAME");
  146. if (t.charAt(0) == '"') {
  147. t = t.substring(1);
  148. }
  149. if (t.charAt(t.length() - 1) == '"') {
  150. t = t.substring(0, t.length() - 1);
  151. }
  152. if (!t.equalsIgnoreCase(iciqlTables)) {
  153. tables.add(new TableInspector(s, t, dateTimeClass));
  154. }
  155. }
  156. }
  157. if (StringUtils.isNullOrEmpty(schema) && StringUtils.isNullOrEmpty(table)) {
  158. // all schemas and tables
  159. return tables;
  160. }
  161. // schema subset OR table subset OR exact match
  162. List<TableInspector> matches = Utils.newArrayList();
  163. for (TableInspector t : tables) {
  164. if (t.matches(schema, table)) {
  165. matches.add(t);
  166. }
  167. }
  168. if (matches.size() == 0) {
  169. throw new IciqlException(MessageFormat.format("Failed to find schema={0} table={1}",
  170. schema == null ? "" : schema, table == null ? "" : table));
  171. }
  172. return matches;
  173. } finally {
  174. JdbcUtils.closeSilently(rs);
  175. }
  176. }
  177. }