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.

DefaultSQLGenerator.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.data.util.sqlcontainer.query.generator;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import com.vaadin.data.Container.Filter;
  9. import com.vaadin.data.util.sqlcontainer.ColumnProperty;
  10. import com.vaadin.data.util.sqlcontainer.RowItem;
  11. import com.vaadin.data.util.sqlcontainer.SQLUtil;
  12. import com.vaadin.data.util.sqlcontainer.TemporaryRowId;
  13. import com.vaadin.data.util.sqlcontainer.query.OrderBy;
  14. import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
  15. import com.vaadin.data.util.sqlcontainer.query.generator.filter.StringDecorator;
  16. /**
  17. * Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL.
  18. *
  19. * @author Jonatan Kronqvist / Vaadin Ltd
  20. */
  21. @SuppressWarnings("serial")
  22. public class DefaultSQLGenerator implements SQLGenerator {
  23. public DefaultSQLGenerator() {
  24. }
  25. /**
  26. * Construct a DefaultSQLGenerator with the specified identifiers for start
  27. * and end of quoted strings. The identifiers may be different depending on
  28. * the database engine and it's settings.
  29. *
  30. * @param quoteStart
  31. * the identifier (character) denoting the start of a quoted
  32. * string
  33. * @param quoteEnd
  34. * the identifier (character) denoting the end of a quoted string
  35. */
  36. public DefaultSQLGenerator(String quoteStart, String quoteEnd) {
  37. QueryBuilder.setStringDecorator(new StringDecorator(quoteStart,
  38. quoteEnd));
  39. }
  40. /*
  41. * (non-Javadoc)
  42. *
  43. * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
  44. * generateSelectQuery(java.lang.String, java.util.List, java.util.List,
  45. * int, int, java.lang.String)
  46. */
  47. @Override
  48. public StatementHelper generateSelectQuery(String tableName,
  49. List<Filter> filters, List<OrderBy> orderBys, int offset,
  50. int pagelength, String toSelect) {
  51. if (tableName == null || tableName.trim().equals("")) {
  52. throw new IllegalArgumentException("Table name must be given.");
  53. }
  54. toSelect = toSelect == null ? "*" : toSelect;
  55. StatementHelper sh = new StatementHelper();
  56. StringBuffer query = new StringBuffer();
  57. query.append("SELECT " + toSelect + " FROM ").append(
  58. SQLUtil.escapeSQL(tableName));
  59. if (filters != null) {
  60. query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
  61. }
  62. if (orderBys != null) {
  63. for (OrderBy o : orderBys) {
  64. generateOrderBy(query, o, orderBys.indexOf(o) == 0);
  65. }
  66. }
  67. if (pagelength != 0) {
  68. generateLimits(query, offset, pagelength);
  69. }
  70. sh.setQueryString(query.toString());
  71. return sh;
  72. }
  73. /*
  74. * (non-Javadoc)
  75. *
  76. * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
  77. * generateUpdateQuery(java.lang.String,
  78. * com.vaadin.addon.sqlcontainer.RowItem)
  79. */
  80. @Override
  81. public StatementHelper generateUpdateQuery(String tableName, RowItem item) {
  82. if (tableName == null || tableName.trim().equals("")) {
  83. throw new IllegalArgumentException("Table name must be given.");
  84. }
  85. if (item == null) {
  86. throw new IllegalArgumentException("Updated item must be given.");
  87. }
  88. StatementHelper sh = new StatementHelper();
  89. StringBuffer query = new StringBuffer();
  90. query.append("UPDATE ").append(tableName).append(" SET");
  91. /* Generate column<->value and rowidentifiers map */
  92. Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
  93. Map<String, Object> rowIdentifiers = generateRowIdentifiers(item);
  94. /* Generate columns and values to update */
  95. boolean first = true;
  96. for (String column : columnToValueMap.keySet()) {
  97. if (first) {
  98. query.append(" " + QueryBuilder.quote(column) + " = ?");
  99. } else {
  100. query.append(", " + QueryBuilder.quote(column) + " = ?");
  101. }
  102. sh.addParameterValue(columnToValueMap.get(column), item
  103. .getItemProperty(column).getType());
  104. first = false;
  105. }
  106. /* Generate identifiers for the row to be updated */
  107. first = true;
  108. for (String column : rowIdentifiers.keySet()) {
  109. if (first) {
  110. query.append(" WHERE " + QueryBuilder.quote(column) + " = ?");
  111. } else {
  112. query.append(" AND " + QueryBuilder.quote(column) + " = ?");
  113. }
  114. sh.addParameterValue(rowIdentifiers.get(column), item
  115. .getItemProperty(column).getType());
  116. first = false;
  117. }
  118. sh.setQueryString(query.toString());
  119. return sh;
  120. }
  121. /*
  122. * (non-Javadoc)
  123. *
  124. * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
  125. * generateInsertQuery(java.lang.String,
  126. * com.vaadin.addon.sqlcontainer.RowItem)
  127. */
  128. @Override
  129. public StatementHelper generateInsertQuery(String tableName, RowItem item) {
  130. if (tableName == null || tableName.trim().equals("")) {
  131. throw new IllegalArgumentException("Table name must be given.");
  132. }
  133. if (item == null) {
  134. throw new IllegalArgumentException("New item must be given.");
  135. }
  136. if (!(item.getId() instanceof TemporaryRowId)) {
  137. throw new IllegalArgumentException(
  138. "Cannot generate an insert query for item already in database.");
  139. }
  140. StatementHelper sh = new StatementHelper();
  141. StringBuffer query = new StringBuffer();
  142. query.append("INSERT INTO ").append(tableName).append(" (");
  143. /* Generate column<->value map */
  144. Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
  145. /* Generate column names for insert query */
  146. boolean first = true;
  147. for (String column : columnToValueMap.keySet()) {
  148. if (!first) {
  149. query.append(", ");
  150. }
  151. query.append(QueryBuilder.quote(column));
  152. first = false;
  153. }
  154. /* Generate values for insert query */
  155. query.append(") VALUES (");
  156. first = true;
  157. for (String column : columnToValueMap.keySet()) {
  158. if (!first) {
  159. query.append(", ");
  160. }
  161. query.append("?");
  162. sh.addParameterValue(columnToValueMap.get(column), item
  163. .getItemProperty(column).getType());
  164. first = false;
  165. }
  166. query.append(")");
  167. sh.setQueryString(query.toString());
  168. return sh;
  169. }
  170. /*
  171. * (non-Javadoc)
  172. *
  173. * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
  174. * generateDeleteQuery(java.lang.String,
  175. * com.vaadin.addon.sqlcontainer.RowItem)
  176. */
  177. @Override
  178. public StatementHelper generateDeleteQuery(String tableName,
  179. List<String> primaryKeyColumns, String versionColumn, RowItem item) {
  180. if (tableName == null || tableName.trim().equals("")) {
  181. throw new IllegalArgumentException("Table name must be given.");
  182. }
  183. if (item == null) {
  184. throw new IllegalArgumentException(
  185. "Item to be deleted must be given.");
  186. }
  187. if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
  188. throw new IllegalArgumentException(
  189. "Valid keyColumnNames must be provided.");
  190. }
  191. StatementHelper sh = new StatementHelper();
  192. StringBuffer query = new StringBuffer();
  193. query.append("DELETE FROM ").append(tableName).append(" WHERE ");
  194. int count = 1;
  195. for (String keyColName : primaryKeyColumns) {
  196. if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
  197. && keyColName.equalsIgnoreCase("rownum")) {
  198. count++;
  199. continue;
  200. }
  201. if (count > 1) {
  202. query.append(" AND ");
  203. }
  204. if (item.getItemProperty(keyColName).getValue() != null) {
  205. query.append(QueryBuilder.quote(keyColName) + " = ?");
  206. sh.addParameterValue(item.getItemProperty(keyColName)
  207. .getValue(), item.getItemProperty(keyColName).getType());
  208. }
  209. count++;
  210. }
  211. if (versionColumn != null) {
  212. query.append(String.format(" AND %s = ?",
  213. QueryBuilder.quote(versionColumn)));
  214. sh.addParameterValue(
  215. item.getItemProperty(versionColumn).getValue(), item
  216. .getItemProperty(versionColumn).getType());
  217. }
  218. sh.setQueryString(query.toString());
  219. return sh;
  220. }
  221. /**
  222. * Generates sorting rules as an ORDER BY -clause
  223. *
  224. * @param sb
  225. * StringBuffer to which the clause is appended.
  226. * @param o
  227. * OrderBy object to be added into the sb.
  228. * @param firstOrderBy
  229. * If true, this is the first OrderBy.
  230. * @return
  231. */
  232. protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o,
  233. boolean firstOrderBy) {
  234. if (firstOrderBy) {
  235. sb.append(" ORDER BY ");
  236. } else {
  237. sb.append(", ");
  238. }
  239. sb.append(QueryBuilder.quote(o.getColumn()));
  240. if (o.isAscending()) {
  241. sb.append(" ASC");
  242. } else {
  243. sb.append(" DESC");
  244. }
  245. return sb;
  246. }
  247. /**
  248. * Generates the LIMIT and OFFSET clause.
  249. *
  250. * @param sb
  251. * StringBuffer to which the clause is appended.
  252. * @param offset
  253. * Value for offset.
  254. * @param pagelength
  255. * Value for pagelength.
  256. * @return StringBuffer with LIMIT and OFFSET clause added.
  257. */
  258. protected StringBuffer generateLimits(StringBuffer sb, int offset,
  259. int pagelength) {
  260. sb.append(" LIMIT ").append(pagelength).append(" OFFSET ")
  261. .append(offset);
  262. return sb;
  263. }
  264. protected Map<String, Object> generateColumnToValueMap(RowItem item) {
  265. Map<String, Object> columnToValueMap = new HashMap<String, Object>();
  266. for (Object id : item.getItemPropertyIds()) {
  267. ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
  268. /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
  269. if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
  270. && cp.getPropertyId().equalsIgnoreCase("rownum")) {
  271. continue;
  272. }
  273. Object value = cp.getValue() == null ? null : cp.getValue();
  274. /* Only include properties whose read-only status can be altered */
  275. if (cp.isReadOnlyChangeAllowed() && !cp.isVersionColumn()) {
  276. columnToValueMap.put(cp.getPropertyId(), value);
  277. }
  278. }
  279. return columnToValueMap;
  280. }
  281. protected Map<String, Object> generateRowIdentifiers(RowItem item) {
  282. Map<String, Object> rowIdentifiers = new HashMap<String, Object>();
  283. for (Object id : item.getItemPropertyIds()) {
  284. ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
  285. /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
  286. if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
  287. && cp.getPropertyId().equalsIgnoreCase("rownum")) {
  288. continue;
  289. }
  290. Object value = cp.getValue() == null ? null : cp.getValue();
  291. if (!cp.isReadOnlyChangeAllowed() || cp.isVersionColumn()) {
  292. rowIdentifiers.put(cp.getPropertyId(), value);
  293. }
  294. }
  295. return rowIdentifiers;
  296. }
  297. }