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 6.1KB

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