PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/src/java/com/googlecode/axolotl/jdbc/GrailsDomainSqlFactory.java

http://axolotl.googlecode.com/
Java | 467 lines | 319 code | 95 blank | 53 comment | 48 complexity | 8fd8dcfb507f86fd93ea265f31eddb02 MD5 | raw file
  1. /*
  2. * Axolotl - Alternate persistence for Grails
  3. * Copyright (C) 2008 Juanjo Garc?­a Latre
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2.1 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this library; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. */
  19. package com.googlecode.axolotl.jdbc;
  20. import java.sql.ResultSet;
  21. import java.sql.SQLException;
  22. import java.sql.Types;
  23. import java.util.ArrayList;
  24. import java.util.Date;
  25. import java.util.List;
  26. import java.util.Map;
  27. import org.codehaus.groovy.grails.commons.DomainClassArtefactHandler;
  28. import org.codehaus.groovy.grails.commons.GrailsApplication;
  29. import org.codehaus.groovy.grails.commons.GrailsDomainClass;
  30. import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty;
  31. import org.codehaus.groovy.grails.plugins.support.aware.GrailsApplicationAware;
  32. import org.springframework.beans.BeanWrapper;
  33. import org.springframework.beans.BeanWrapperImpl;
  34. import org.springframework.jdbc.core.PreparedStatementCreator;
  35. import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
  36. import org.springframework.jdbc.core.RowMapper;
  37. import org.springframework.jdbc.core.SqlTypeValue;
  38. import org.springframework.util.CachingMapDecorator;
  39. import com.googlecode.axolotl.Database;
  40. import com.googlecode.axolotl.grails.GrailsDomainUtils;
  41. import com.googlecode.axolotl.lazy.LazyEntity;
  42. import com.googlecode.axolotl.lazy.LazyEntitySet;
  43. import com.googlecode.axolotl.query.Criterion;
  44. import com.googlecode.axolotl.query.Equals;
  45. import com.googlecode.axolotl.query.Order;
  46. import com.googlecode.axolotl.query.Query;
  47. public class GrailsDomainSqlFactory implements SqlFactory, GrailsApplicationAware {
  48. private GrailsApplication application;
  49. private Map convertersCache = new CachingMapDecorator() {
  50. protected Object create(Object clazz) {
  51. return new GrailsDomainConverter((Class) clazz);
  52. }
  53. };
  54. public void setGrailsApplication(GrailsApplication application) {
  55. this.application = application;
  56. }
  57. public String getTableName(Class clazz) {
  58. GrailsDomainClass domainClass = getDomainClass(clazz);
  59. return getTableName(domainClass);
  60. }
  61. public String getColumnName(Class clazz, String property) {
  62. GrailsDomainClass domainClass = getDomainClass(clazz);
  63. return getColumnName( domainClass.getPropertyByName(property) );
  64. }
  65. public String getSelectRow(Class clazz) {
  66. GrailsDomainClass domainClass = getDomainClass(clazz);
  67. StringBuilder sql = new StringBuilder("select ")
  68. .append( getColumnName(domainClass.getIdentifier()) );
  69. GrailsDomainClassProperty[] properties = getPersistentProperties(domainClass);
  70. for (int i = 0 ; i < properties.length ; i++) {
  71. sql.append(",").append( getColumnName(properties[i]) );
  72. }
  73. sql.append(" from ").append( getTableName(domainClass) )
  74. .append(" where ").append( getColumnName( domainClass.getIdentifier() ) ).append("=?");
  75. return sql.toString();
  76. }
  77. public String getSelectCount(Class clazz, Object criteria) {
  78. GrailsDomainClass domainClass = getDomainClass(clazz);
  79. GrailsDomainSqlCriteriaBuilder criteriaBuilder =
  80. new GrailsDomainSqlCriteriaBuilder(application, domainClass, this);
  81. CharSequence where = buildWhere(criteriaBuilder, criteria);
  82. StringBuilder sql = new StringBuilder(512);
  83. sql.append("select count(distinct ");
  84. sql.append( getTableName(domainClass) )
  85. .append('.').append( getColumnName( domainClass.getIdentifier() ) );
  86. sql.append(") from ").append( buildFrom(domainClass, criteriaBuilder) );
  87. if (where.length() > 0) {
  88. sql.append(" where ").append(where);
  89. }
  90. return sql.toString();
  91. }
  92. public String getSelectIds(Class clazz, Object criteria, Object order) {
  93. GrailsDomainClass domainClass = getDomainClass(clazz);
  94. GrailsDomainSqlCriteriaBuilder criteriaBuilder =
  95. new GrailsDomainSqlCriteriaBuilder(application, domainClass, this);
  96. CharSequence orderBy = buildOrderBy(domainClass, criteriaBuilder, order);
  97. CharSequence where = buildWhere(criteriaBuilder, criteria);
  98. StringBuilder sql = new StringBuilder(512);
  99. sql.append("select ");
  100. if (criteriaBuilder.needsDistinct()) {
  101. sql.append("distinct ");
  102. }
  103. sql.append( getTableName(domainClass) )
  104. .append('.').append( getColumnName( domainClass.getIdentifier() ) );
  105. if (criteriaBuilder.needsDistinct()) {
  106. for (String o: criteriaBuilder.getOrders()) {
  107. sql.append(", ").append(o);
  108. }
  109. }
  110. sql.append(" from ").append( buildFrom(domainClass, criteriaBuilder) );
  111. if (where.length() > 0) {
  112. sql.append(" where ").append(where);
  113. }
  114. if (orderBy.length() > 0) {
  115. sql.append(" order by ").append(orderBy);
  116. }
  117. return sql.toString();
  118. }
  119. public String getInsertRow(Class clazz) {
  120. GrailsDomainClass domainClass = getDomainClass(clazz);
  121. GrailsDomainClassProperty[] properties = getPersistentProperties(domainClass);
  122. StringBuilder sql = new StringBuilder()
  123. .append("insert into ").append( getTableName(domainClass) )
  124. .append(" (");
  125. for (int i = 0 ; i < properties.length ; i++) {
  126. sql.append(i==0 ? "" : ",").append( getColumnName(properties[i]) );
  127. }
  128. sql.append(") values (");
  129. for (int i = 0 ; i < properties.length ; i++) {
  130. sql.append(i==0 ? "" : ",").append('?');
  131. }
  132. sql.append(")");
  133. return sql.toString();
  134. }
  135. public String getUpdateRow(Class clazz) {
  136. GrailsDomainClass domainClass = getDomainClass(clazz);
  137. StringBuilder sql = new StringBuilder()
  138. .append("update ").append( getTableName(domainClass) )
  139. .append(" set ");
  140. GrailsDomainClassProperty[] properties = getPersistentProperties(domainClass);
  141. for (int i = 0; i < properties.length; i++) {
  142. sql.append( i==0 ? "" : "," );
  143. sql.append( getColumnName(properties[i]) ).append("=?");
  144. }
  145. sql.append(" where ").append( getColumnName( domainClass.getIdentifier() ) ).append("=?");
  146. return sql.toString();
  147. }
  148. public String getDeleteRow(Class clazz) {
  149. GrailsDomainClass domainClass = getDomainClass(clazz);
  150. StringBuilder sql = new StringBuilder()
  151. .append("delete from ").append( getTableName(domainClass) )
  152. .append(" where ").append( getColumnName( domainClass.getIdentifier() ) ).append("=?");
  153. return sql.toString();
  154. }
  155. public Converter getConverter(Class clazz) {
  156. return (Converter) convertersCache.get(clazz);
  157. }
  158. //-------------------------------------------------------------------------------------------
  159. protected class GrailsDomainConverter implements Converter {
  160. private final GrailsDomainClass domainClass;
  161. private final String identifierColumn;
  162. private final PreparedStatementCreatorFactory preparedInsertCreatorFactory;
  163. private final PreparedStatementCreatorFactory preparedUpdateCreatorFactory;
  164. private final PreparedStatementCreatorFactory preparedDeleteCreatorFactory;
  165. private Class[] subclasses;
  166. public GrailsDomainConverter(Class clazz) {
  167. this.domainClass = getDomainClass(clazz);
  168. this.identifierColumn = getColumnName( domainClass.getIdentifier() );
  169. this.preparedInsertCreatorFactory =
  170. new PreparedStatementCreatorFactory( getInsertRow(clazz), getPersistentTypes(domainClass) );
  171. this.preparedUpdateCreatorFactory =
  172. new PreparedStatementCreatorFactory( getUpdateRow(clazz), getPersistentTypesWithId(domainClass) );
  173. this.preparedDeleteCreatorFactory =
  174. new PreparedStatementCreatorFactory( getDeleteRow(clazz), new int[] {getPersistentType(domainClass.getIdentifier())} );
  175. }
  176. public Class[] getSubClasses() {
  177. if (subclasses == null) {
  178. if (domainClass.hasSubClasses()) {
  179. List subDomainClasses = new ArrayList( domainClass.getSubClasses() );
  180. subclasses = new Class[ subDomainClasses.size() ];
  181. for (int i = 0; i < subDomainClasses.size(); i++) {
  182. subclasses[ i ] = ((GrailsDomainClass) subDomainClasses.get(i)).getClazz();
  183. }
  184. }
  185. else {
  186. subclasses = new Class[0];
  187. }
  188. }
  189. return subclasses;
  190. }
  191. public RowMapper getEntityReader(final Database database) {
  192. return new RowMapper() {
  193. public Object mapRow(ResultSet rs, int row) throws SQLException {
  194. BeanWrapper instance = new BeanWrapperImpl( domainClass.getClazz() );
  195. final Object id = rs.getObject(identifierColumn);
  196. instance.setPropertyValue(domainClass.getIdentifier().getName(), id);
  197. for (GrailsDomainClassProperty property: domainClass.getPersistentProperties()) {
  198. Object value = null;
  199. final Class refType = property.getReferencedPropertyType();
  200. if (!property.isOneToMany() && !property.isManyToMany()) {
  201. String columnName = getColumnName(property);
  202. value = rs.getObject(columnName);
  203. if (property.isManyToOne() || property.isOneToOne()) {
  204. if (property.getFetchMode() == GrailsDomainClassProperty.FETCH_LAZY) {
  205. value = LazyEntity.create(refType, value, database);
  206. }
  207. else if (property.getFetchMode() == GrailsDomainClassProperty.FETCH_EAGER) {
  208. value = database.load(refType, value);
  209. }
  210. }
  211. }
  212. else if (property.isOneToMany()) {
  213. String keyName = property.getOtherSide().getName();
  214. Object keyValue = instance.getWrappedInstance();
  215. Query query = new Query(refType, new Equals<Object>(keyName, keyValue), null);
  216. value = new LazyEntitySet(query, database);
  217. }
  218. instance.setPropertyValue(property.getName(), value);
  219. }
  220. return instance.getWrappedInstance();
  221. }
  222. };
  223. }
  224. public RowMapper getIdReader() {
  225. return new RowMapper() {
  226. public Object mapRow(ResultSet rs, int row) throws SQLException {
  227. return rs.getObject(1); //identifierColumn); TODO?
  228. }
  229. };
  230. }
  231. public PreparedStatementCreator getInsertStatementCreator(Object entity) {
  232. List values = GrailsDomainUtils.getPersistentValues(domainClass, entity);
  233. return preparedInsertCreatorFactory.newPreparedStatementCreator(values);
  234. }
  235. public PreparedStatementCreator getUpdateStatementCreator(Object entity) {
  236. List values = GrailsDomainUtils.getPersistentValues(domainClass, entity);
  237. values.add( GrailsDomainUtils.getIdentifierValue(domainClass, entity) );
  238. return preparedUpdateCreatorFactory.newPreparedStatementCreator(values);
  239. }
  240. public PreparedStatementCreator getDeleteStatementCreator(Object entity) {
  241. Object id = GrailsDomainUtils.getIdentifierValue(domainClass, entity);
  242. return preparedDeleteCreatorFactory.newPreparedStatementCreator( new Object[] {id} );
  243. }
  244. // private List getPersistentValues(Object entity) {
  245. // BeanWrapper wrapper = (entity instanceof BeanWrapper) ? (BeanWrapper) entity : new BeanWrapperImpl(entity);
  246. // List<Object> values = new ArrayList<Object>();
  247. // for (GrailsDomainClassProperty property : getPersistentProperties(domainClass)) {
  248. // Object value = wrapper.getPropertyValue( property.getName() );
  249. // if ((property.isManyToOne() || property.isOneToOne()) && value != null) {
  250. // if (LazyEntity.isLazy(value)) {
  251. // value = LazyEntity.getId(value);
  252. // }
  253. // else {
  254. // value = GrailsDomainUtils.getIdentifierValue(
  255. // property.getReferencedDomainClass(), value);
  256. // }
  257. // }
  258. // values.add(value);
  259. // }
  260. // return values;
  261. // }
  262. }
  263. protected String getTableName(GrailsDomainClass domainClass) {
  264. return SqlNamingUtils.getTableName(domainClass);
  265. // return underscorize( domainClass.getName() );
  266. }
  267. protected String[] getColumnNames(GrailsDomainClassProperty[] properties) {
  268. String[] columnNames = new String[ properties.length ];
  269. for (int i = 0; i < columnNames.length; i++) {
  270. columnNames[i] = getColumnName( properties[i] );
  271. }
  272. return columnNames;
  273. }
  274. protected String getColumnName(GrailsDomainClassProperty property) {
  275. return SqlNamingUtils.getColumnName(property);
  276. // StringBuilder name = new StringBuilder( underscorize(property.getName()) );
  277. // if (property.isManyToOne() || property.isOneToOne()) {
  278. // name.append("_id");
  279. // }
  280. // return name.toString();
  281. }
  282. @Deprecated
  283. protected GrailsDomainClass getDomainClass(Class clazz) {
  284. return (GrailsDomainClass) application.getArtefact(DomainClassArtefactHandler.TYPE, clazz.getName());
  285. }
  286. @Deprecated
  287. protected GrailsDomainClassProperty[] getPersistentProperties(GrailsDomainClass domainClass) {
  288. return GrailsDomainUtils.getPersistentProperties(domainClass);
  289. // List<GrailsDomainClassProperty> properties = new ArrayList<GrailsDomainClassProperty>();
  290. // for (GrailsDomainClassProperty property: domainClass.getPersistentProperties()) {
  291. // if (!property.isOneToMany() && !property.isManyToMany()) {
  292. // properties.add(property);
  293. // }
  294. // }
  295. // return properties.toArray(new GrailsDomainClassProperty[properties.size()]);
  296. }
  297. protected int[] getPersistentTypes(GrailsDomainClass domainClass) {
  298. GrailsDomainClassProperty[] properties = getPersistentProperties(domainClass);
  299. int[] types = new int[ properties.length ];
  300. for (int i = 0; i < properties.length; i++) {
  301. types[ i ] = getPersistentType( properties[ i ] );
  302. }
  303. return types;
  304. }
  305. protected int[] getPersistentTypesWithId(GrailsDomainClass domainClass) {
  306. int[] persistentTypes = getPersistentTypes(domainClass);
  307. int[] typesWithId = new int[ persistentTypes.length + 1 ];
  308. System.arraycopy(persistentTypes, 0, typesWithId, 0, persistentTypes.length);
  309. typesWithId[ typesWithId.length - 1 ] = getPersistentType( domainClass.getIdentifier() );
  310. return typesWithId;
  311. }
  312. protected int getPersistentType(GrailsDomainClassProperty property) {
  313. Class type = property.getType();
  314. if (Number.class.isAssignableFrom(type)) {
  315. return Types.NUMERIC;
  316. }
  317. else if (CharSequence.class.isAssignableFrom(type)) {
  318. return Types.VARCHAR;
  319. }
  320. else if (Date.class.isAssignableFrom(type)) {
  321. return Types.TIMESTAMP;
  322. }
  323. else {
  324. return SqlTypeValue.TYPE_UNKNOWN;
  325. }
  326. }
  327. private CharSequence buildFrom(GrailsDomainClass domainClass, GrailsDomainSqlCriteriaBuilder criteriaBuilder) {
  328. StringBuilder from = new StringBuilder( getTableName(domainClass) );
  329. // append join tables found by criteria builder.
  330. for (GrailsDomainSqlCriteriaBuilder.JoinInfo join: criteriaBuilder.getJoins()) {
  331. from.append(", ").append( join.getTable() );
  332. }
  333. return from;
  334. }
  335. private CharSequence buildWhere(GrailsDomainSqlCriteriaBuilder criteriaBuilder, Object criteria) {
  336. String filter = "";
  337. if (criteria instanceof Criterion) {
  338. filter = ((Criterion) criteria).accept(criteriaBuilder);
  339. }
  340. else if (criteria != null) {
  341. filter = criteria.toString();
  342. }
  343. StringBuilder where = new StringBuilder();
  344. // append join criteria found by renderer.
  345. for (GrailsDomainSqlCriteriaBuilder.JoinInfo join: criteriaBuilder.getJoins()) {
  346. if (where.length() > 0) {
  347. where.append(" and ");
  348. }
  349. where.append( join.getCriterion() );
  350. }
  351. // if there was a previous filter, then it should be appended after join criteria.
  352. if (filter.length() > 0) {
  353. if (where.length() > 0) {
  354. where.append(" and ");
  355. }
  356. where.append(filter);
  357. }
  358. return where;
  359. }
  360. private CharSequence buildOrderBy(GrailsDomainClass domainClass,
  361. GrailsDomainSqlCriteriaBuilder criteriaBuilder, Object order) {
  362. StringBuilder orderBy = new StringBuilder();
  363. final Object o = order;//(order == null ? domainClass.getDefaultOrder() : order);
  364. if (o instanceof Order) {
  365. orderBy.append( ((Order) o).accept(criteriaBuilder) );
  366. }
  367. else if (o != null) {
  368. orderBy.append(o);
  369. }
  370. return orderBy;
  371. }
  372. }