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.

QueryContainer.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.data.util;
  5. import java.sql.Connection;
  6. import java.sql.ResultSet;
  7. import java.sql.ResultSetMetaData;
  8. import java.sql.SQLException;
  9. import java.sql.Statement;
  10. import java.util.ArrayList;
  11. import java.util.Collection;
  12. import java.util.Collections;
  13. import java.util.HashMap;
  14. import com.vaadin.data.Container;
  15. import com.vaadin.data.Item;
  16. import com.vaadin.data.Property;
  17. /**
  18. * <p>
  19. * The <code>QueryContainer</code> is the specialized form of Container which is
  20. * Ordered and Indexed. This is used to represent the contents of relational
  21. * database tables accessed through the JDBC Connection in the Vaadin Table.
  22. * This creates Items based on the queryStatement provided to the container.
  23. * </p>
  24. *
  25. * <p>
  26. * The <code>QueryContainer</code> can be visualized as a representation of a
  27. * relational database table.Each Item in the container represents the row
  28. * fetched by the query.All cells in a column have same data type and the data
  29. * type information is retrieved from the metadata of the resultset.
  30. * </p>
  31. *
  32. * <p>
  33. * Note : If data in the tables gets modified, Container will not get reflected
  34. * with the updates, we have to explicity invoke QueryContainer.refresh method.
  35. * {@link com.vaadin.data.util.QueryContainer#refresh() refresh()}
  36. * </p>
  37. *
  38. * @see com.vaadin.data.Container
  39. *
  40. * @author IT Mill Ltd.
  41. * @version
  42. * @since 4.0
  43. */
  44. @SuppressWarnings("serial")
  45. public class QueryContainer implements Container, Container.Ordered,
  46. Container.Indexed {
  47. // default ResultSet type
  48. public static final int DEFAULT_RESULTSET_TYPE = ResultSet.TYPE_SCROLL_INSENSITIVE;
  49. // default ResultSet concurrency
  50. public static final int DEFAULT_RESULTSET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY;
  51. private int resultSetType = DEFAULT_RESULTSET_TYPE;
  52. private int resultSetConcurrency = DEFAULT_RESULTSET_CONCURRENCY;
  53. private final String queryStatement;
  54. private final Connection connection;
  55. private ResultSet result;
  56. private Collection propertyIds;
  57. private final HashMap propertyTypes = new HashMap();
  58. private int size = -1;
  59. private Statement statement;
  60. /**
  61. * Constructs new <code>QueryContainer</code> with the specified
  62. * <code>queryStatement</code>.
  63. *
  64. * @param queryStatement
  65. * Database query
  66. * @param connection
  67. * Connection object
  68. * @param resultSetType
  69. * @param resultSetConcurrency
  70. * @throws SQLException
  71. * when database operation fails
  72. */
  73. public QueryContainer(String queryStatement, Connection connection,
  74. int resultSetType, int resultSetConcurrency) throws SQLException {
  75. this.queryStatement = queryStatement;
  76. this.connection = connection;
  77. this.resultSetType = resultSetType;
  78. this.resultSetConcurrency = resultSetConcurrency;
  79. init();
  80. }
  81. /**
  82. * Constructs new <code>QueryContainer</code> with the specified
  83. * queryStatement using the default resultset type and default resultset
  84. * concurrency.
  85. *
  86. * @param queryStatement
  87. * Database query
  88. * @param connection
  89. * Connection object
  90. * @see QueryContainer#DEFAULT_RESULTSET_TYPE
  91. * @see QueryContainer#DEFAULT_RESULTSET_CONCURRENCY
  92. * @throws SQLException
  93. * when database operation fails
  94. */
  95. public QueryContainer(String queryStatement, Connection connection)
  96. throws SQLException {
  97. this(queryStatement, connection, DEFAULT_RESULTSET_TYPE,
  98. DEFAULT_RESULTSET_CONCURRENCY);
  99. }
  100. /**
  101. * Fills the Container with the items and properties. Invoked by the
  102. * constructor.
  103. *
  104. * @throws SQLException
  105. * when parameter initialization fails.
  106. * @see QueryContainer#QueryContainer(String, Connection, int, int).
  107. */
  108. private void init() throws SQLException {
  109. refresh();
  110. ResultSetMetaData metadata;
  111. metadata = result.getMetaData();
  112. final int count = metadata.getColumnCount();
  113. final ArrayList list = new ArrayList(count);
  114. for (int i = 1; i <= count; i++) {
  115. final String columnName = metadata.getColumnName(i);
  116. list.add(columnName);
  117. final Property p = getContainerProperty(new Integer(1), columnName);
  118. propertyTypes.put(columnName,
  119. p == null ? Object.class : p.getType());
  120. }
  121. propertyIds = Collections.unmodifiableCollection(list);
  122. }
  123. /**
  124. * <p>
  125. * Restores items in the container. This method will update the latest data
  126. * to the container.
  127. * </p>
  128. * Note: This method should be used to update the container with the latest
  129. * items.
  130. *
  131. * @throws SQLException
  132. * when database operation fails
  133. *
  134. */
  135. public void refresh() throws SQLException {
  136. close();
  137. statement = connection.createStatement(resultSetType,
  138. resultSetConcurrency);
  139. result = statement.executeQuery(queryStatement);
  140. result.last();
  141. size = result.getRow();
  142. }
  143. /**
  144. * Releases and nullifies the <code>statement</code>.
  145. *
  146. * @throws SQLException
  147. * when database operation fails
  148. */
  149. public void close() throws SQLException {
  150. if (statement != null) {
  151. statement.close();
  152. }
  153. statement = null;
  154. }
  155. /**
  156. * Gets the Item with the given Item ID from the Container.
  157. *
  158. * @param id
  159. * ID of the Item to retrieve
  160. * @return Item Id.
  161. */
  162. public Item getItem(Object id) {
  163. return new Row(id);
  164. }
  165. /**
  166. * Gets the collection of propertyId from the Container.
  167. *
  168. * @return Collection of Property ID.
  169. */
  170. public Collection getContainerPropertyIds() {
  171. return propertyIds;
  172. }
  173. /**
  174. * Gets an collection of all the item IDs in the container.
  175. *
  176. * @return collection of Item IDs
  177. */
  178. public Collection getItemIds() {
  179. final Collection c = new ArrayList(size);
  180. for (int i = 1; i <= size; i++) {
  181. c.add(new Integer(i));
  182. }
  183. return c;
  184. }
  185. /**
  186. * Gets the property identified by the given itemId and propertyId from the
  187. * container. If the container does not contain the property
  188. * <code>null</code> is returned.
  189. *
  190. * @param itemId
  191. * ID of the Item which contains the Property
  192. * @param propertyId
  193. * ID of the Property to retrieve
  194. *
  195. * @return Property with the given ID if exists; <code>null</code>
  196. * otherwise.
  197. */
  198. public synchronized Property getContainerProperty(Object itemId,
  199. Object propertyId) {
  200. if (!(itemId instanceof Integer && propertyId instanceof String)) {
  201. return null;
  202. }
  203. Object value;
  204. try {
  205. result.absolute(((Integer) itemId).intValue());
  206. value = result.getObject((String) propertyId);
  207. } catch (final Exception e) {
  208. return null;
  209. }
  210. // Handle also null values from the database
  211. return new ObjectProperty(value != null ? value : new String(""));
  212. }
  213. /**
  214. * Gets the data type of all properties identified by the given type ID.
  215. *
  216. * @param id
  217. * ID identifying the Properties
  218. *
  219. * @return data type of the Properties
  220. */
  221. public Class getType(Object id) {
  222. return (Class) propertyTypes.get(id);
  223. }
  224. /**
  225. * Gets the number of items in the container.
  226. *
  227. * @return the number of items in the container.
  228. */
  229. public int size() {
  230. return size;
  231. }
  232. /**
  233. * Tests if the list contains the specified Item.
  234. *
  235. * @param id
  236. * ID the of Item to be tested.
  237. * @return <code>true</code> if given id is in the container;
  238. * <code>false</code> otherwise.
  239. */
  240. public boolean containsId(Object id) {
  241. if (!(id instanceof Integer)) {
  242. return false;
  243. }
  244. final int i = ((Integer) id).intValue();
  245. if (i < 1) {
  246. return false;
  247. }
  248. if (i > size) {
  249. return false;
  250. }
  251. return true;
  252. }
  253. /**
  254. * Creates new Item with the given ID into the Container.
  255. *
  256. * @param itemId
  257. * ID of the Item to be created.
  258. *
  259. * @return Created new Item, or <code>null</code> if it fails.
  260. *
  261. * @throws UnsupportedOperationException
  262. * if the addItem method is not supported.
  263. */
  264. public Item addItem(Object itemId) throws UnsupportedOperationException {
  265. throw new UnsupportedOperationException();
  266. }
  267. /**
  268. * Creates a new Item into the Container, and assign it an ID.
  269. *
  270. * @return ID of the newly created Item, or <code>null</code> if it fails.
  271. * @throws UnsupportedOperationException
  272. * if the addItem method is not supported.
  273. */
  274. public Object addItem() throws UnsupportedOperationException {
  275. throw new UnsupportedOperationException();
  276. }
  277. /**
  278. * Removes the Item identified by ItemId from the Container.
  279. *
  280. * @param itemId
  281. * ID of the Item to remove.
  282. * @return <code>true</code> if the operation succeeded; <code>false</code>
  283. * otherwise.
  284. * @throws UnsupportedOperationException
  285. * if the removeItem method is not supported.
  286. */
  287. public boolean removeItem(Object itemId)
  288. throws UnsupportedOperationException {
  289. throw new UnsupportedOperationException();
  290. }
  291. /**
  292. * Adds new Property to all Items in the Container.
  293. *
  294. * @param propertyId
  295. * ID of the Property
  296. * @param type
  297. * Data type of the new Property
  298. * @param defaultValue
  299. * The value all created Properties are initialized to.
  300. * @return <code>true</code> if the operation succeeded; <code>false</code>
  301. * otherwise.
  302. * @throws UnsupportedOperationException
  303. * if the addContainerProperty method is not supported.
  304. */
  305. public boolean addContainerProperty(Object propertyId, Class<?> type,
  306. Object defaultValue) throws UnsupportedOperationException {
  307. throw new UnsupportedOperationException();
  308. }
  309. /**
  310. * Removes a Property specified by the given Property ID from the Container.
  311. *
  312. * @param propertyId
  313. * ID of the Property to remove
  314. * @return <code>true</code> if the operation succeeded; <code>false</code>
  315. * otherwise.
  316. * @throws UnsupportedOperationException
  317. * if the removeContainerProperty method is not supported.
  318. */
  319. public boolean removeContainerProperty(Object propertyId)
  320. throws UnsupportedOperationException {
  321. throw new UnsupportedOperationException();
  322. }
  323. /**
  324. * Removes all Items from the Container.
  325. *
  326. * @return <code>true</code> if the operation succeeded; <code>false</code>
  327. * otherwise.
  328. * @throws UnsupportedOperationException
  329. * if the removeAllItems method is not supported.
  330. */
  331. public boolean removeAllItems() throws UnsupportedOperationException {
  332. throw new UnsupportedOperationException();
  333. }
  334. /**
  335. * Adds new item after the given item.
  336. *
  337. * @param previousItemId
  338. * Id of the previous item in ordered container.
  339. * @param newItemId
  340. * Id of the new item to be added.
  341. * @return Returns new item or <code>null</code> if the operation fails.
  342. * @throws UnsupportedOperationException
  343. * if the addItemAfter method is not supported.
  344. */
  345. public Item addItemAfter(Object previousItemId, Object newItemId)
  346. throws UnsupportedOperationException {
  347. throw new UnsupportedOperationException();
  348. }
  349. /**
  350. * Adds new item after the given item.
  351. *
  352. * @param previousItemId
  353. * Id of the previous item in ordered container.
  354. * @return Returns item id created new item or <code>null</code> if the
  355. * operation fails.
  356. * @throws UnsupportedOperationException
  357. * if the addItemAfter method is not supported.
  358. */
  359. public Object addItemAfter(Object previousItemId)
  360. throws UnsupportedOperationException {
  361. throw new UnsupportedOperationException();
  362. }
  363. /**
  364. * Returns id of first item in the Container.
  365. *
  366. * @return ID of the first Item in the list.
  367. */
  368. public Object firstItemId() {
  369. if (size < 1) {
  370. return null;
  371. }
  372. return new Integer(1);
  373. }
  374. /**
  375. * Returns <code>true</code> if given id is first id at first index.
  376. *
  377. * @param id
  378. * ID of an Item in the Container.
  379. */
  380. public boolean isFirstId(Object id) {
  381. return size > 0 && (id instanceof Integer)
  382. && ((Integer) id).intValue() == 1;
  383. }
  384. /**
  385. * Returns <code>true</code> if given id is last id at last index.
  386. *
  387. * @param id
  388. * ID of an Item in the Container
  389. *
  390. */
  391. public boolean isLastId(Object id) {
  392. return size > 0 && (id instanceof Integer)
  393. && ((Integer) id).intValue() == size;
  394. }
  395. /**
  396. * Returns id of last item in the Container.
  397. *
  398. * @return ID of the last Item.
  399. */
  400. public Object lastItemId() {
  401. if (size < 1) {
  402. return null;
  403. }
  404. return new Integer(size);
  405. }
  406. /**
  407. * Returns id of next item in container at next index.
  408. *
  409. * @param id
  410. * ID of an Item in the Container.
  411. * @return ID of the next Item or null.
  412. */
  413. public Object nextItemId(Object id) {
  414. if (size < 1 || !(id instanceof Integer)) {
  415. return null;
  416. }
  417. final int i = ((Integer) id).intValue();
  418. if (i >= size) {
  419. return null;
  420. }
  421. return new Integer(i + 1);
  422. }
  423. /**
  424. * Returns id of previous item in container at previous index.
  425. *
  426. * @param id
  427. * ID of an Item in the Container.
  428. * @return ID of the previous Item or null.
  429. */
  430. public Object prevItemId(Object id) {
  431. if (size < 1 || !(id instanceof Integer)) {
  432. return null;
  433. }
  434. final int i = ((Integer) id).intValue();
  435. if (i <= 1) {
  436. return null;
  437. }
  438. return new Integer(i - 1);
  439. }
  440. /**
  441. * The <code>Row</code> class implements methods of Item.
  442. *
  443. * @author IT Mill Ltd.
  444. * @version
  445. * @since 4.0
  446. */
  447. class Row implements Item {
  448. Object id;
  449. private Row(Object rowId) {
  450. id = rowId;
  451. }
  452. /**
  453. * Adds the item property.
  454. *
  455. * @param id
  456. * ID of the new Property.
  457. * @param property
  458. * Property to be added and associated with ID.
  459. * @return <code>true</code> if the operation succeeded;
  460. * <code>false</code> otherwise.
  461. * @throws UnsupportedOperationException
  462. * if the addItemProperty method is not supported.
  463. */
  464. public boolean addItemProperty(Object id, Property property)
  465. throws UnsupportedOperationException {
  466. throw new UnsupportedOperationException();
  467. }
  468. /**
  469. * Gets the property corresponding to the given property ID stored in
  470. * the Item.
  471. *
  472. * @param propertyId
  473. * identifier of the Property to get
  474. * @return the Property with the given ID or <code>null</code>
  475. */
  476. public Property getItemProperty(Object propertyId) {
  477. return getContainerProperty(id, propertyId);
  478. }
  479. /**
  480. * Gets the collection of property IDs stored in the Item.
  481. *
  482. * @return unmodifiable collection containing IDs of the Properties
  483. * stored the Item.
  484. */
  485. public Collection getItemPropertyIds() {
  486. return propertyIds;
  487. }
  488. /**
  489. * Removes given item property.
  490. *
  491. * @param id
  492. * ID of the Property to be removed.
  493. * @return <code>true</code> if the item property is removed;
  494. * <code>false</code> otherwise.
  495. * @throws UnsupportedOperationException
  496. * if the removeItemProperty is not supported.
  497. */
  498. public boolean removeItemProperty(Object id)
  499. throws UnsupportedOperationException {
  500. throw new UnsupportedOperationException();
  501. }
  502. }
  503. /**
  504. * Closes the statement.
  505. *
  506. * @see #close()
  507. */
  508. @Override
  509. public void finalize() {
  510. try {
  511. close();
  512. } catch (final SQLException ignored) {
  513. }
  514. }
  515. /**
  516. * Adds the given item at the position of given index.
  517. *
  518. * @param index
  519. * Index to add the new item.
  520. * @param newItemId
  521. * Id of the new item to be added.
  522. * @return new item or <code>null</code> if the operation fails.
  523. * @throws UnsupportedOperationException
  524. * if the addItemAt is not supported.
  525. */
  526. public Item addItemAt(int index, Object newItemId)
  527. throws UnsupportedOperationException {
  528. throw new UnsupportedOperationException();
  529. }
  530. /**
  531. * Adds item at the position of provided index in the container.
  532. *
  533. * @param index
  534. * Index to add the new item.
  535. * @return item id created new item or <code>null</code> if the operation
  536. * fails.
  537. *
  538. * @throws UnsupportedOperationException
  539. * if the addItemAt is not supported.
  540. */
  541. public Object addItemAt(int index) throws UnsupportedOperationException {
  542. throw new UnsupportedOperationException();
  543. }
  544. /**
  545. * Gets the Index id in the container.
  546. *
  547. * @param index
  548. * Index Id.
  549. * @return ID in the given index.
  550. */
  551. public Object getIdByIndex(int index) {
  552. if (size < 1 || index < 0 || index >= size) {
  553. return null;
  554. }
  555. return new Integer(index + 1);
  556. }
  557. /**
  558. * Gets the index of the Item corresponding to id in the container.
  559. *
  560. * @param id
  561. * ID of an Item in the Container
  562. * @return index of the Item, or -1 if the Container does not include the
  563. * Item
  564. */
  565. public int indexOfId(Object id) {
  566. if (size < 1 || !(id instanceof Integer)) {
  567. return -1;
  568. }
  569. final int i = ((Integer) id).intValue();
  570. if (i >= size || i < 1) {
  571. return -1;
  572. }
  573. return i - 1;
  574. }
  575. }