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.

FreeformQuery.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  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
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  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
  14. * the License.
  15. */
  16. package com.vaadin.v7.data.util.sqlcontainer.query;
  17. import java.io.IOException;
  18. import java.sql.Connection;
  19. import java.sql.PreparedStatement;
  20. import java.sql.ResultSet;
  21. import java.sql.SQLException;
  22. import java.sql.Statement;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import com.vaadin.v7.data.Container.Filter;
  28. import com.vaadin.v7.data.util.sqlcontainer.RowItem;
  29. import com.vaadin.v7.data.util.sqlcontainer.SQLContainer;
  30. import com.vaadin.v7.data.util.sqlcontainer.connection.JDBCConnectionPool;
  31. import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper;
  32. import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
  33. /**
  34. * @deprecated As of 8.0, no replacement available.
  35. */
  36. @SuppressWarnings("serial")
  37. @Deprecated
  38. public class FreeformQuery extends AbstractTransactionalQuery
  39. implements QueryDelegate {
  40. FreeformQueryDelegate delegate = null;
  41. private String queryString;
  42. private List<String> primaryKeyColumns;
  43. /**
  44. * Prevent no-parameters instantiation of FreeformQuery
  45. */
  46. @SuppressWarnings("unused")
  47. private FreeformQuery() {
  48. }
  49. /**
  50. * Creates a new freeform query delegate to be used with the
  51. * {@link SQLContainer}.
  52. *
  53. * @param queryString
  54. * The actual query to perform.
  55. * @param primaryKeyColumns
  56. * The primary key columns. Read-only mode is forced if this
  57. * parameter is null or empty.
  58. * @param connectionPool
  59. * the JDBCConnectionPool to use to open connections to the SQL
  60. * database.
  61. * @deprecated As of 6.7, @see
  62. * {@link FreeformQuery#FreeformQuery(String, JDBCConnectionPool, String...)}
  63. */
  64. @Deprecated
  65. public FreeformQuery(String queryString, List<String> primaryKeyColumns,
  66. JDBCConnectionPool connectionPool) {
  67. super(connectionPool);
  68. if (primaryKeyColumns == null) {
  69. primaryKeyColumns = new ArrayList<String>();
  70. }
  71. if (primaryKeyColumns.contains("")) {
  72. throw new IllegalArgumentException(
  73. "The primary key columns contain an empty string!");
  74. } else if (queryString == null || "".equals(queryString)) {
  75. throw new IllegalArgumentException(
  76. "The query string may not be empty or null!");
  77. } else if (connectionPool == null) {
  78. throw new IllegalArgumentException(
  79. "The connectionPool may not be null!");
  80. }
  81. this.queryString = queryString;
  82. this.primaryKeyColumns = Collections
  83. .unmodifiableList(primaryKeyColumns);
  84. }
  85. /**
  86. * Creates a new freeform query delegate to be used with the
  87. * {@link SQLContainer}.
  88. *
  89. * @param queryString
  90. * The actual query to perform.
  91. * @param connectionPool
  92. * the JDBCConnectionPool to use to open connections to the SQL
  93. * database.
  94. * @param primaryKeyColumns
  95. * The primary key columns. Read-only mode is forced if none are
  96. * provided. (optional)
  97. */
  98. public FreeformQuery(String queryString, JDBCConnectionPool connectionPool,
  99. String... primaryKeyColumns) {
  100. this(queryString, Arrays.asList(primaryKeyColumns), connectionPool);
  101. }
  102. /**
  103. * This implementation of getCount() actually fetches all records from the
  104. * database, which might be a performance issue. Override this method with a
  105. * SELECT COUNT(*) ... query if this is too slow for your needs.
  106. *
  107. * {@inheritDoc}
  108. */
  109. @Override
  110. public int getCount() throws SQLException {
  111. // First try the delegate
  112. int count = countByDelegate();
  113. if (count < 0) {
  114. // Couldn't use the delegate, use the bad way.
  115. Statement statement = null;
  116. ResultSet rs = null;
  117. Connection conn = getConnection();
  118. try {
  119. statement = conn.createStatement(
  120. ResultSet.TYPE_SCROLL_INSENSITIVE,
  121. ResultSet.CONCUR_READ_ONLY);
  122. rs = statement.executeQuery(queryString);
  123. if (rs.last()) {
  124. count = rs.getRow();
  125. } else {
  126. count = 0;
  127. }
  128. } finally {
  129. releaseConnection(conn, statement, rs);
  130. }
  131. }
  132. return count;
  133. }
  134. @SuppressWarnings("deprecation")
  135. private int countByDelegate() throws SQLException {
  136. int count = -1;
  137. if (delegate == null) {
  138. return count;
  139. }
  140. /* First try using prepared statement */
  141. if (delegate instanceof FreeformStatementDelegate) {
  142. try {
  143. StatementHelper sh = ((FreeformStatementDelegate) delegate)
  144. .getCountStatement();
  145. PreparedStatement pstmt = null;
  146. ResultSet rs = null;
  147. Connection c = getConnection();
  148. try {
  149. pstmt = c.prepareStatement(sh.getQueryString());
  150. sh.setParameterValuesToStatement(pstmt);
  151. rs = pstmt.executeQuery();
  152. if (rs.next()) {
  153. count = rs.getInt(1);
  154. } else {
  155. // The result can be empty when using group by and there
  156. // are no matches (#18043)
  157. count = 0;
  158. }
  159. } finally {
  160. releaseConnection(c, pstmt, rs);
  161. }
  162. return count;
  163. } catch (UnsupportedOperationException e) {
  164. // Count statement generation not supported
  165. }
  166. }
  167. /* Try using regular statement */
  168. try {
  169. String countQuery = delegate.getCountQuery();
  170. if (countQuery != null) {
  171. Statement statement = null;
  172. ResultSet rs = null;
  173. Connection conn = getConnection();
  174. try {
  175. statement = conn.createStatement();
  176. rs = statement.executeQuery(countQuery);
  177. if (rs.next()) {
  178. count = rs.getInt(1);
  179. } else {
  180. // The result can be empty when using group by and there
  181. // are no matches (#18043)
  182. count = 0;
  183. }
  184. return count;
  185. } finally {
  186. releaseConnection(conn, statement, rs);
  187. }
  188. }
  189. } catch (UnsupportedOperationException e) {
  190. // Count query generation not supported
  191. }
  192. return count;
  193. }
  194. /**
  195. * Fetches the results for the query. This implementation always fetches the
  196. * entire record set, ignoring the offset and page length parameters. In
  197. * order to support lazy loading of records, you must supply a
  198. * FreeformQueryDelegate that implements the
  199. * FreeformQueryDelegate.getQueryString(int,int) method.
  200. *
  201. * @throws SQLException
  202. *
  203. * @see FreeformQueryDelegate#getQueryString(int, int)
  204. */
  205. @Override
  206. @SuppressWarnings({ "deprecation", "finally" })
  207. public ResultSet getResults(int offset, int pagelength)
  208. throws SQLException {
  209. ensureTransaction();
  210. String query = queryString;
  211. if (delegate != null) {
  212. /* First try using prepared statement */
  213. if (delegate instanceof FreeformStatementDelegate) {
  214. try {
  215. StatementHelper sh = ((FreeformStatementDelegate) delegate)
  216. .getQueryStatement(offset, pagelength);
  217. PreparedStatement pstmt = getConnection()
  218. .prepareStatement(sh.getQueryString());
  219. sh.setParameterValuesToStatement(pstmt);
  220. return pstmt.executeQuery();
  221. } catch (UnsupportedOperationException e) {
  222. // Statement generation not supported, continue...
  223. }
  224. }
  225. try {
  226. query = delegate.getQueryString(offset, pagelength);
  227. } catch (UnsupportedOperationException e) {
  228. // This is fine, we'll just use the default queryString.
  229. }
  230. }
  231. Statement statement = getConnection().createStatement();
  232. ResultSet rs;
  233. try {
  234. rs = statement.executeQuery(query);
  235. } catch (SQLException e) {
  236. try {
  237. statement.close();
  238. } finally {
  239. // throw the original exception even if closing the statement
  240. // fails
  241. throw e;
  242. }
  243. }
  244. return rs;
  245. }
  246. @Override
  247. @SuppressWarnings("deprecation")
  248. public boolean implementationRespectsPagingLimits() {
  249. if (delegate == null) {
  250. return false;
  251. }
  252. /* First try using prepared statement */
  253. if (delegate instanceof FreeformStatementDelegate) {
  254. try {
  255. StatementHelper sh = ((FreeformStatementDelegate) delegate)
  256. .getCountStatement();
  257. if (sh != null && sh.getQueryString() != null
  258. && !sh.getQueryString().isEmpty()) {
  259. return true;
  260. }
  261. } catch (UnsupportedOperationException e) {
  262. // Statement generation not supported, continue...
  263. }
  264. }
  265. try {
  266. String queryString = delegate.getQueryString(0, 50);
  267. return queryString != null && !queryString.isEmpty();
  268. } catch (UnsupportedOperationException e) {
  269. return false;
  270. }
  271. }
  272. /*
  273. * (non-Javadoc)
  274. *
  275. * @see
  276. * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setFilters(java
  277. * .util.List)
  278. */
  279. @Override
  280. public void setFilters(List<Filter> filters)
  281. throws UnsupportedOperationException {
  282. if (delegate != null) {
  283. delegate.setFilters(filters);
  284. } else if (filters != null) {
  285. throw new UnsupportedOperationException(
  286. "FreeFormQueryDelegate not set!");
  287. }
  288. }
  289. /*
  290. * (non-Javadoc)
  291. *
  292. * @see
  293. * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setOrderBy(java
  294. * .util.List)
  295. */
  296. @Override
  297. public void setOrderBy(List<OrderBy> orderBys)
  298. throws UnsupportedOperationException {
  299. if (delegate != null) {
  300. delegate.setOrderBy(orderBys);
  301. } else if (orderBys != null) {
  302. throw new UnsupportedOperationException(
  303. "FreeFormQueryDelegate not set!");
  304. }
  305. }
  306. /*
  307. * (non-Javadoc)
  308. *
  309. * @see
  310. * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin
  311. * .data.util.sqlcontainer.RowItem)
  312. */
  313. @Override
  314. public int storeRow(RowItem row) throws SQLException {
  315. if (!isInTransaction()) {
  316. throw new IllegalStateException("No transaction is active!");
  317. } else if (primaryKeyColumns.isEmpty()) {
  318. throw new UnsupportedOperationException(
  319. "Cannot store items fetched with a read-only freeform query!");
  320. }
  321. if (delegate != null) {
  322. return delegate.storeRow(getConnection(), row);
  323. } else {
  324. throw new UnsupportedOperationException(
  325. "FreeFormQueryDelegate not set!");
  326. }
  327. }
  328. /*
  329. * (non-Javadoc)
  330. *
  331. * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate#removeRow(com.
  332. * vaadin .data.util.sqlcontainer.RowItem)
  333. */
  334. @Override
  335. public boolean removeRow(RowItem row) throws SQLException {
  336. if (!isInTransaction()) {
  337. throw new IllegalStateException("No transaction is active!");
  338. } else if (primaryKeyColumns.isEmpty()) {
  339. throw new UnsupportedOperationException(
  340. "Cannot remove items fetched with a read-only freeform query!");
  341. }
  342. if (delegate != null) {
  343. return delegate.removeRow(getConnection(), row);
  344. } else {
  345. throw new UnsupportedOperationException(
  346. "FreeFormQueryDelegate not set!");
  347. }
  348. }
  349. @Override
  350. public synchronized void beginTransaction()
  351. throws UnsupportedOperationException, SQLException {
  352. super.beginTransaction();
  353. }
  354. @Override
  355. public synchronized void commit()
  356. throws UnsupportedOperationException, SQLException {
  357. super.commit();
  358. }
  359. @Override
  360. public synchronized void rollback()
  361. throws UnsupportedOperationException, SQLException {
  362. super.rollback();
  363. }
  364. /*
  365. * (non-Javadoc)
  366. *
  367. * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate#
  368. * getPrimaryKeyColumns ()
  369. */
  370. @Override
  371. public List<String> getPrimaryKeyColumns() {
  372. return primaryKeyColumns;
  373. }
  374. public String getQueryString() {
  375. return queryString;
  376. }
  377. public FreeformQueryDelegate getDelegate() {
  378. return delegate;
  379. }
  380. public void setDelegate(FreeformQueryDelegate delegate) {
  381. this.delegate = delegate;
  382. }
  383. /**
  384. * This implementation of the containsRowWithKey method rewrites existing
  385. * WHERE clauses in the query string. The logic is, however, not very
  386. * complex and some times can do the Wrong Thing<sup>TM</sup>. For the
  387. * situations where this logic is not enough, you can implement the
  388. * getContainsRowQueryString method in FreeformQueryDelegate and this will
  389. * be used instead of the logic.
  390. *
  391. * @see FreeformQueryDelegate#getContainsRowQueryString(Object...)
  392. *
  393. */
  394. @Override
  395. @SuppressWarnings("deprecation")
  396. public boolean containsRowWithKey(Object... keys) throws SQLException {
  397. String query = null;
  398. boolean contains = false;
  399. if (delegate != null) {
  400. if (delegate instanceof FreeformStatementDelegate) {
  401. try {
  402. StatementHelper sh = ((FreeformStatementDelegate) delegate)
  403. .getContainsRowQueryStatement(keys);
  404. PreparedStatement pstmt = null;
  405. ResultSet rs = null;
  406. Connection c = getConnection();
  407. try {
  408. pstmt = c.prepareStatement(sh.getQueryString());
  409. sh.setParameterValuesToStatement(pstmt);
  410. rs = pstmt.executeQuery();
  411. contains = rs.next();
  412. return contains;
  413. } finally {
  414. releaseConnection(c, pstmt, rs);
  415. }
  416. } catch (UnsupportedOperationException e) {
  417. // Statement generation not supported, continue...
  418. }
  419. }
  420. try {
  421. query = delegate.getContainsRowQueryString(keys);
  422. } catch (UnsupportedOperationException e) {
  423. query = modifyWhereClause(keys);
  424. }
  425. } else {
  426. query = modifyWhereClause(keys);
  427. }
  428. Statement statement = null;
  429. ResultSet rs = null;
  430. Connection conn = getConnection();
  431. try {
  432. statement = conn.createStatement();
  433. rs = statement.executeQuery(query);
  434. contains = rs.next();
  435. } finally {
  436. releaseConnection(conn, statement, rs);
  437. }
  438. return contains;
  439. }
  440. private String modifyWhereClause(Object... keys) {
  441. // Build the where rules for the provided keys
  442. StringBuilder where = new StringBuilder();
  443. for (int ix = 0; ix < primaryKeyColumns.size(); ix++) {
  444. where.append(QueryBuilder.quote(primaryKeyColumns.get(ix)));
  445. if (keys[ix] == null) {
  446. where.append(" IS NULL");
  447. } else {
  448. where.append(" = '").append(keys[ix]).append('\'');
  449. }
  450. if (ix < primaryKeyColumns.size() - 1) {
  451. where.append(" AND ");
  452. }
  453. }
  454. // Is there already a WHERE clause in the query string?
  455. int index = queryString.toLowerCase().indexOf("where ");
  456. if (index > -1) {
  457. // Rewrite the where clause
  458. return queryString.substring(0, index) + "WHERE " + where + " AND "
  459. + queryString.substring(index + 6);
  460. }
  461. // Append a where clause
  462. return queryString + " WHERE " + where;
  463. }
  464. private void writeObject(java.io.ObjectOutputStream out)
  465. throws IOException {
  466. try {
  467. rollback();
  468. } catch (SQLException ignored) {
  469. }
  470. out.defaultWriteObject();
  471. }
  472. }