2 * Copyright 2000-2018 Vaadin Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
16 package com.vaadin.v7.data.util.sqlcontainer.query.generator;
18 import java.util.HashMap;
19 import java.util.List;
22 import com.vaadin.v7.data.Container.Filter;
23 import com.vaadin.v7.data.util.sqlcontainer.ColumnProperty;
24 import com.vaadin.v7.data.util.sqlcontainer.RowItem;
25 import com.vaadin.v7.data.util.sqlcontainer.SQLUtil;
26 import com.vaadin.v7.data.util.sqlcontainer.TemporaryRowId;
27 import com.vaadin.v7.data.util.sqlcontainer.query.OrderBy;
28 import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
29 import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.StringDecorator;
32 * Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL.
34 * @author Jonatan Kronqvist / Vaadin Ltd
36 * @deprecated As of 8.0, no replacement available.
38 @SuppressWarnings("serial")
40 public class DefaultSQLGenerator implements SQLGenerator {
42 private Class<? extends StatementHelper> statementHelperClass = null;
44 public DefaultSQLGenerator() {
49 * Create a new DefaultSqlGenerator instance that uses the given
50 * implementation of {@link StatementHelper}.
52 * @param statementHelper
54 public DefaultSQLGenerator(
55 Class<? extends StatementHelper> statementHelperClazz) {
57 statementHelperClass = statementHelperClazz;
61 * Construct a DefaultSQLGenerator with the specified identifiers for start
62 * and end of quoted strings. The identifiers may be different depending on
63 * the database engine and it's settings.
66 * the identifier (character) denoting the start of a quoted
69 * the identifier (character) denoting the end of a quoted string
71 public DefaultSQLGenerator(String quoteStart, String quoteEnd) {
73 .setStringDecorator(new StringDecorator(quoteStart, quoteEnd));
77 * Same as {@link #DefaultSQLGenerator(String, String)} but with support for
78 * custom {@link StatementHelper} implementation.
82 * @param statementHelperClazz
84 public DefaultSQLGenerator(String quoteStart, String quoteEnd,
85 Class<? extends StatementHelper> statementHelperClazz) {
86 this(quoteStart, quoteEnd);
87 statementHelperClass = statementHelperClazz;
93 * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
94 * generateSelectQuery(java.lang.String, java.util.List, java.util.List,
95 * int, int, java.lang.String)
98 public StatementHelper generateSelectQuery(String tableName,
99 List<Filter> filters, List<OrderBy> orderBys, int offset,
100 int pagelength, String toSelect) {
101 if (tableName == null || tableName.trim().equals("")) {
102 throw new IllegalArgumentException("Table name must be given.");
104 toSelect = toSelect == null ? "*" : toSelect;
105 StatementHelper sh = getStatementHelper();
106 StringBuffer query = new StringBuffer();
107 query.append("SELECT " + toSelect + " FROM ")
108 .append(SQLUtil.escapeSQL(tableName));
109 if (filters != null) {
110 query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
112 if (orderBys != null) {
113 for (OrderBy o : orderBys) {
114 generateOrderBy(query, o, orderBys.indexOf(o) == 0);
117 if (pagelength != 0) {
118 generateLimits(query, offset, pagelength);
120 sh.setQueryString(query.toString());
127 * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
128 * generateUpdateQuery(java.lang.String,
129 * com.vaadin.addon.sqlcontainer.RowItem)
132 public StatementHelper generateUpdateQuery(String tableName, RowItem item) {
133 if (tableName == null || tableName.trim().equals("")) {
134 throw new IllegalArgumentException("Table name must be given.");
137 throw new IllegalArgumentException("Updated item must be given.");
139 StatementHelper sh = getStatementHelper();
140 StringBuilder query = new StringBuilder();
141 query.append("UPDATE ").append(tableName).append(" SET");
143 /* Generate column<->value and rowidentifiers map */
144 Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
145 Map<String, Object> rowIdentifiers = generateRowIdentifiers(item);
146 /* Generate columns and values to update */
147 boolean first = true;
148 for (String column : columnToValueMap.keySet()) {
150 query.append(" " + QueryBuilder.quote(column) + " = ?");
152 query.append(", " + QueryBuilder.quote(column) + " = ?");
154 sh.addParameterValue(columnToValueMap.get(column),
155 item.getItemProperty(column).getType());
158 /* Generate identifiers for the row to be updated */
160 for (String column : rowIdentifiers.keySet()) {
162 query.append(" WHERE " + QueryBuilder.quote(column) + " = ?");
164 query.append(" AND " + QueryBuilder.quote(column) + " = ?");
166 sh.addParameterValue(rowIdentifiers.get(column),
167 item.getItemProperty(column).getType());
170 sh.setQueryString(query.toString());
177 * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
178 * generateInsertQuery(java.lang.String,
179 * com.vaadin.addon.sqlcontainer.RowItem)
182 public StatementHelper generateInsertQuery(String tableName, RowItem item) {
183 if (tableName == null || tableName.trim().equals("")) {
184 throw new IllegalArgumentException("Table name must be given.");
187 throw new IllegalArgumentException("New item must be given.");
189 if (!(item.getId() instanceof TemporaryRowId)) {
190 throw new IllegalArgumentException(
191 "Cannot generate an insert query for item already in database.");
193 StatementHelper sh = getStatementHelper();
194 StringBuilder query = new StringBuilder();
195 query.append("INSERT INTO ").append(tableName).append(" (");
197 /* Generate column<->value map */
198 Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
199 /* Generate column names for insert query */
200 boolean first = true;
201 for (String column : columnToValueMap.keySet()) {
205 query.append(QueryBuilder.quote(column));
209 /* Generate values for insert query */
210 query.append(") VALUES (");
212 for (String column : columnToValueMap.keySet()) {
217 sh.addParameterValue(columnToValueMap.get(column),
218 item.getItemProperty(column).getType());
222 sh.setQueryString(query.toString());
229 * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
230 * generateDeleteQuery(java.lang.String,
231 * com.vaadin.addon.sqlcontainer.RowItem)
234 public StatementHelper generateDeleteQuery(String tableName,
235 List<String> primaryKeyColumns, String versionColumn,
237 if (tableName == null || tableName.trim().equals("")) {
238 throw new IllegalArgumentException("Table name must be given.");
241 throw new IllegalArgumentException(
242 "Item to be deleted must be given.");
244 if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
245 throw new IllegalArgumentException(
246 "Valid keyColumnNames must be provided.");
248 StatementHelper sh = getStatementHelper();
249 StringBuilder query = new StringBuilder();
250 query.append("DELETE FROM ").append(tableName).append(" WHERE ");
252 for (String keyColName : primaryKeyColumns) {
253 if ((this instanceof MSSQLGenerator
254 || this instanceof OracleGenerator)
255 && keyColName.equalsIgnoreCase("rownum")) {
260 query.append(" AND ");
262 if (item.getItemProperty(keyColName).getValue() != null) {
263 query.append(QueryBuilder.quote(keyColName) + " = ?");
264 sh.addParameterValue(
265 item.getItemProperty(keyColName).getValue(),
266 item.getItemProperty(keyColName).getType());
270 if (versionColumn != null) {
271 if (!item.getItemPropertyIds().contains(versionColumn)) {
272 throw new IllegalArgumentException(String.format(
273 "Table '%s' does not contain version column '%s'.",
274 tableName, versionColumn));
277 query.append(String.format(" AND %s = ?",
278 QueryBuilder.quote(versionColumn)));
279 sh.addParameterValue(item.getItemProperty(versionColumn).getValue(),
280 item.getItemProperty(versionColumn).getType());
283 sh.setQueryString(query.toString());
288 * Generates sorting rules as an ORDER BY -clause.
291 * StringBuffer to which the clause is appended.
293 * OrderBy object to be added into the sb.
294 * @param firstOrderBy
295 * If true, this is the first OrderBy.
298 protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o,
299 boolean firstOrderBy) {
301 sb.append(" ORDER BY ");
305 sb.append(QueryBuilder.quote(o.getColumn()));
306 if (o.isAscending()) {
315 * Generates the LIMIT and OFFSET clause.
318 * StringBuffer to which the clause is appended.
322 * Value for pagelength.
323 * @return StringBuffer with LIMIT and OFFSET clause added.
325 protected StringBuffer generateLimits(StringBuffer sb, int offset,
327 sb.append(" LIMIT ").append(pagelength).append(" OFFSET ")
332 protected Map<String, Object> generateColumnToValueMap(RowItem item) {
333 Map<String, Object> columnToValueMap = new HashMap<String, Object>();
334 for (Object id : item.getItemPropertyIds()) {
335 ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
336 /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
337 if ((this instanceof MSSQLGenerator
338 || this instanceof OracleGenerator)
339 && cp.getPropertyId().equalsIgnoreCase("rownum")) {
342 if (cp.isPersistent()) {
343 columnToValueMap.put(cp.getPropertyId(), cp.getValue());
346 return columnToValueMap;
349 protected Map<String, Object> generateRowIdentifiers(RowItem item) {
350 Map<String, Object> rowIdentifiers = new HashMap<String, Object>();
351 for (Object id : item.getItemPropertyIds()) {
352 ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
353 /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
354 if ((this instanceof MSSQLGenerator
355 || this instanceof OracleGenerator)
356 && cp.getPropertyId().equalsIgnoreCase("rownum")) {
360 if (cp.isRowIdentifier()) {
362 if (cp.isPrimaryKey()) {
363 // If the value of a primary key has changed, its old value
364 // should be used to identify the row (#9145)
365 value = cp.getOldValue();
367 value = cp.getValue();
369 rowIdentifiers.put(cp.getPropertyId(), value);
372 return rowIdentifiers;
376 * Returns the statement helper for the generator. Override this to handle
377 * platform specific data types.
379 * @see http://dev.vaadin.com/ticket/9148
380 * @return a new instance of the statement helper
382 protected StatementHelper getStatementHelper() {
383 if (statementHelperClass == null) {
384 return new StatementHelper();
388 return statementHelperClass.newInstance();
389 } catch (InstantiationException e) {
390 throw new RuntimeException(
391 "Unable to instantiate custom StatementHelper", e);
392 } catch (IllegalAccessException e) {
393 throw new RuntimeException(
394 "Unable to instantiate custom StatementHelper", e);