Download Vaadin SLQContainer 0.8 User Manual Table of Contents

Transcript
Vaadin SLQContainer 0.8 User Manual
Teppo Kurki
© 2010 Vaadin Ltd.
Table of Contents
1. Introduction....................................................................................................................................2
2. Architecture.....................................................................................................................................3
3. Getting started with SQLContainer.............................................................................................5
3.1. Creating a connection pool.......................................................................................................5
3.2. Creating the TableQuery query delegate...................................................................................5
3.3. Creating the container...............................................................................................................5
4. Using the SQLContainer................................................................................................................6
4.1. Filtering and Sorting.................................................................................................................6
4.1.1. Filtering............................................................................................................................6
4.1.2. Filtering mode..................................................................................................................6
4.1.3. Sorting...............................................................................................................................7
4.2. Editing.......................................................................................................................................7
4.2.1. Adding items.....................................................................................................................7
4.2.2. Fetching generated row keys............................................................................................7
4.2.3. Version column requirement.............................................................................................8
4.2.3. Autocommit mode.............................................................................................................8
4.2.4. Modified state...................................................................................................................8
4.3. Caching, paging and refreshing................................................................................................8
4.3.1. Container size...................................................................................................................9
4.3.2. Page length and cache size...............................................................................................9
4.3.3. Refreshing the container...................................................................................................9
5. Using FreeformQuery and FreeformQueryDelegate................................................................10
5.1. Getting started.........................................................................................................................10
5.2. Limitations..............................................................................................................................10
5.3. Creating your own FreeformQueryDelegate..........................................................................10
6. Non-implemented methods of Vaadin container interfaces......................................................11
6.1. About the getItemIds() method...............................................................................................11
7. Appendices.....................................................................................................................................12
A. Supported databases..................................................................................................................12
B. Known issues and limitations of SQLContainer.......................................................................12
C. Planned features........................................................................................................................12
1
Vaadin SQLContainer 0.8 User Manual
1. Introduction
Vaadin SQLContainer is a Vaadin container implementation that allows easy and customizable
access to data stored in various SQL-speaking databases (see appendix A for details). SQLContainer
supports two types of database access. Using TableQuery, the pre-made query generators will
enable fetching, updating and inserting data directly from the container into a database table automatically, whereas FreeformQuery allows the developer to use their own, probably more
complex query for fetching data and their own optional implementations for writing, filtering and
sorting support - item and property handling as well as lazy loading will still be handled
automatically.
In addition to the customizable database connection options, SQLContainer also extends the Vaadin
container interface to implement a bit more advanced and more database oriented filtering rules.
Finally, the add-on also offers connection pool implementations for JDBC connection pooling and
JEE connection pooling, as well as integrated transaction support; auto-commit mode is also
provided.
The purpose of this manual is to briefly explain the architecture and some of the inner workings of
SQLContainer. It will also give the readers some examples on how to use SQLContainer in their
own applications. The requirements, limitations and further development ideas are also discussed.
2
Vaadin SQLContainer 0.8 User Manual
2. Architecture
The architecture of SQLContainer is relatively simple, and it is described in detail in this section.
SQLContainer is the class implementing the Vaadin container interfaces and providing access to
most of the functionality of this add-on. The standard Vaadin Properties and Items have been
extended by the ColumnProperty and RowItem classes. Item IDs are represented by RowId
and TemporaryRowId classes. The RowId class is built based on the primary key columns of the
connected database table or query result.
In the connection package the JDBCConnectionPool interface defines the requirements for a
connection pool implementation. Two implementations of this interface are provided:
SimpleJDBCConnectionPool provides a simple´yet very usable implementation to pool and
access JDBC connections. J2EEConnectionPool provides means to access J2EE DataSources.
The query package contains the QueryDelegate interface which defines everything the
SQLContainer needs to enable reading and writing data to and from a database. As discussed
earlier, two implementations of this interface are provided: TableQuery for automatic read-write
support for a database table, and FreeformQuery for customizing the query, sorting, filtering
and writing; this is done by implementing relevant methods of the FreeformQueryDelegate
interface.
The query package also contains Filter and OrderBy classes which have been written to
provide an alternative to the standard Vaadin container filtering and make sorting non-String
properties a bit more user friendly.
Finally, the generator package contains a SQLGenerator interface which defines the kind of queries
that are required by the TableQuery class. The provided implementations include support for
HSQLDB, MySQL, PostgreSQL (DefaultSQLGenerator), Oracle (OracleGenerator)
and Microsoft SQL Server (MSSQLGenerator). A new or modified implementation may be
provided to gain compatibility with older versions or other database servers.
A class diagram of SQLContainer and its supporting classes as well as connections to Vaadin
interfaces is provided in figure 1. Note that not every method is shown in the diagram due to space
constraints. For further detail, refer to the SQLContainer API documentation.
3
Vaadin SQLContainer 0.8 User Manual
Filter
FilteringMode
+FILTERING_MODE_INCLUSIVE
+FILTERING_MODE_EXCLUSIVE
1
<<interface>>
Container.ItemSetChangeNotifier
-column: String
-value: Object
-secondValue: Object
-comparisonType: ComparisonType
-isCaseSensitive: boolean
+needsQuotes: boolean
0..*
<<interface>>
Property
OrderBy
<<interface>>
Item
-column: String
-isAscending: boolean
0..*
ColumnProperty
-allowReadOnlyChange: boolean
-owner: RowItem
1..*
<<interface>>
Container.Filterable
TemporaryRowId
SQLContainer
<<interface>>
+refresh(): void
+setAutoCommit(autoCommitEnabled:boolean): void
+isAutoCommit(): boolean
+setPageLength(pageLength:int): void
+getPageLength(): void
+commit(): void
+rollback(): void
+addFilter(filter:Filter): void
+addOrderBy(orderBy:OrderBy): void
+setFilteringMode(filteringMode:FilteringMode): void
+isModified(): boolean
Container.Ordered
<<interface>>
Container.Sortable
<<interface>>
Container.Indexed
<<interface>>
Container
RowItem
RowId
1
+getId(): RowId
+getContainer(): SQLContainer
+isModified(): boolean
0..*
OracleGenerator
MSSQLGenerator
DefaultSQLGenerator
<<interface>>
QueryDelegate
1
Implemented by the user
for sorting, filtering
and write support
+getCount(): int
+getResults(offset:int,pagelength:int): ResultSet
+setFilters(filters:List<Filter>): void
+setOrderBy(orderBys:List<OrderBy>): void
+storeRow(row:RowItem): int
+beginTransaction(): void
+rollback(): void
+commit(): void
+getPrimaryKeyColumns(): List<String>
+implementationRespectsPagingLimits(): boolean
+setFilters(filters:List<Filter>,filteringMode:FilteringMode): void
+removeRow(row:RowItem): boolean
+containsRowWithKey(keys:Object...): boolean
<<interface>>
FreeformQueryDelegate
+setFilters(filters:List<Filter>): void
+setOrderBy(orderBys:List<OrderBy>): void
+storeRow(row:RowItem): int
0..1
<<interface>>
<<interface>>
SQLGenerator
+generateSelectQuery(tableName:String,filters:List<Filter>,
orderBys:List<OrderBy>,
offset:int,pageLength:int,
toSelect:String): String
+generateSelectQuery(tableName:String,filters:List<Filter>,
filterMode:FilteringMode,
orderBys:List<OrderBy>,
offset:int,pagelength:int,
toSelect:int): String
+generateUpdateQuery(tableName:String,item:RowItem): String
+generateInsertQuery(tableName:String,item:RowItem): String
+generateDeleteQuery(tableName:String,item:RowItem): String
1
QueryDelegate.RowIdChangeEvent
<<interface>>
QueryDelegate.RowIdChangeListener
<<interface>>
QueryDelegate.RowIdChangeNotifier
FreeformQuery
TableQuery
-queryString: String
-tableName: String
-sqlGenerator: SQLGenerator
+setDelegate(delegate:FreeFormQueryDelegate): void
1
<<interface>>
JDBCConnectionPool
J2EEConnectionPool
+reserveConnection(): Connection
+releaseConnection(conn:Connection): void
+destroy(): void
Figure 1. Detailed class diagram of SQLContainer and its supporting classes
4
SimpleJDBCConnectionPool
-initialConnections: int
-maxConnections: int
-connectionUri: String
-userName: String
-password: String
Vaadin SQLContainer 0.8 User Manual
3. Getting started with SQLContainer
Gettting development going with the SQLContainer is easy and quite straight-forward. The purpose
of this chapter is to describe how to create the required resources and how to fetch data from and
write data to a database table attached to the container.
3.1. Creating a connection pool
First, we need to create a connection pool to allow the SQLContainer to connect to a database. Here
we will use the SimpleJDBCConnectionPool, which is a basic implementation of connection
pooling with JDBC data sources. In the following code we create a connection pool that uses the
HSQLDB driver together with an in-memory database. The initial amount of connections is 2 and
the maximum amount is set at 5. Note that the database driver, connection url, username and
password parameters will vary depending on the database you are using.
JDBCConnectionPool connectionPool = new SimpleJDBCConnectionPool(
"org.hsqldb.jdbc.JDBCDriver",
"jdbc:hsqldb:mem:sqlcontainer", "SA", "", 2, 5);
3.2. Creating the TableQuery query delegate
After the connection pool is created, we'll need a query delegate for the SQLContainer. The
simplest way to create one is by using the built-in TableQuery class. The TableQuery delegate
provides access to a defined database table and supports reading and writing data out-of-the-box.
We create the TableQuery with the following statement:
TableQuery tq = new TableQuery("tablename", connectionPool);
If we need to enable the write support, we must set a version column to the TableQuery as well. The
version column is an integer or timestamp typed column which will either be incremented or set to
the current time on each modification of the row. TableQuery assumes that the database will take
care of updating the version column; it just makes sure the column value is correct before updating
a row. The following code will set the version column:
tq.setVersionColumn("OPTLOCK");
3.3. Creating the container
Finally we may create the container itself. This is as simple as stating:
SQLContainer container = new SQLContainer(tq);
After this statement the SQLContainer is connected to the table tablename and is ready to use for
example as a data source for a Vaadin Table or a Vaadin Form.
5
Vaadin SQLContainer 0.8 User Manual
4. Using the SQLContainer
4.1. Filtering and Sorting
Filtering and sorting the items contained in an SQLContainer is by design always performed in the
database. In practice this means that whenever the filtering or sorting rules are modified, at least
some amount of database communication will take place (the minimum is to fetch the updated row
count using the new filtering/sorting rules).
4.1.1. Filtering
Filtering can be performed either using the Vaadin-provided means implemented from
Container.Filterable using the following method, where propertyId means column name
in the SQLContainer context. More information on the standard filtering can be found in Book of
Vaadin.
public void addContainerFilter(Object propertyId, String filterString,
boolean ignoreCase, boolean onlyMatchPrefix)
Note! Using the standard method only allows filtering on String-typed properties. Numeric types,
dates etc. must be filtered using the Filter class (see below).
In addition to the standard method, it is also possible to directly add a Filter to the container via
the addFilter(Filter filter) method. This enables the developer to take advantage of a
few more features, including:
•
•
•
•
More comparison methods via Filter.ComparisonType
• EQUALS, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL
• STARTS_WITH, ENDS_WITH, CONTAINS
• BETWEEN
Filtering of numeric and other non-String types
Two-valued filtering via Filter.setSecondValue(Object secondValue)
Implicit setting for need of quoting via setNeedsQuotes(boolean needsQuotes)
Removing the filtering rules is also done via the standard Vaadin methods:
public void removeContainerFilters(Object propertyId)
public void removeAllContainerFilters()
These methods will remove filters added with either addFilter or addContainerFilter
method.
4.1.2. Filtering mode
Currentle the SQLContainer has limited support for two filtering modes. The modes are defined in
the FilteringMode enum which is located in the query package. The two modes are called
exclusive and inclusive. Exlusive mode means that in the generated query all the filtering rules will
be joined with an OR. Inclusivu mode means that AND will be used.
The default filtering mode is inclusive (AND) filtering.
6
Vaadin SQLContainer 0.8 User Manual
4.1.3. Sorting
Sorting can be performed either using the Vaadin-provided means implemented from
Container.Sortable using the following method, where the propertyIds again refer to
column names. More information on the standard filtering can be found in Book of Vaadin.
public void sort(Object[] propertyId, boolean[] ascending)
In addition to the standard method, it is also possible to directly add an OrderBy to the container
via the addOrderBy(OrderBy orderBy) method. This enables the developer to insert sorters
one by one without providing the whole array of them at once.
Sorting rules can be cleared by calling the sort method with null or an empty array as the first
argument.
4.2. Editing
Editing the items (RowItems) of SQLContainer can be done similarly to editing the items of any
Vaadin container. ColumnProperties of a RowItem will automatically notify SQLContainer to make
sure that changes to the items are recorded and will be applied to the database immediately or on
commit, depending on the state of the autocommit mode.
4.2.1. Adding items
Adding items to the SQLContainer can only be done via the addItem() method. This method
will create a new Item based on the connected database table column properties. The new item will
either be buffered by the container or commited to the database through the querydelegate
depending on whether the auto commit mode (see 4.2.3.) has been enabled.
When an item is added to the container it is impossible to precisely know what the primary keys of
the row will be, or will the row insertion succeed at all. This is why the SQLContainer will assign
an instance of TemporaryRowId as a RowId for the new item. See 4.2.2. on how to fetch the
actual key after the row insertion has succeeded.
Note! Currently the addItem() method will return a temporary row ID for the added item even if
auto commit mode is enabled.
4.2.2. Fetching generated row keys
Since it is a common need to fetch the generated key of a row right after insertion, a listener/notifier
has been added into the QueryDelegate interface. Currently only the TableQuery class
implements the RowIdChangeNotifier interface, and thus can notify interested objects of
changed row IDs. The events fill be fired after TableQuery.commit() has finished; this method is
called by SQLContainer when necessary.
To receive updates on the row IDs, you might use the following code (assuming container is an
instance of SQLContainer):
app.getDbHelp().getCityContainer().addListener(
new QueryDelegate.RowIdChangeListener() {
public void rowIdChange(RowIdChangeEvent event) {
System.err.println("Old ID is: " + event.getOldRowId());
System.err.println("New ID is: " + event.getNewRowId());
}
});
7
Vaadin SQLContainer 0.8 User Manual
4.2.3. Version column requirement
If you are using the TableQuery class as the query delegate to the SQLContainer and need to enable
write support, there is an enforced requirement of specifying a version column name to the
TableQuery instance. The column name can be set to the TableQuery using the following statement:
tq.setVersionColumn("OPTLOCK");
The version column is preferrably an integer or timestamp typed column in the table that is attached
to the TableQuery. This column will be used for optimistic locking; before a row modification the
TableQuery will check before that the version column value is the same as it was when the data was
read into the container. This should ensure that no one has modified the row inbetween the current
user's reads and writes.
Note! TableQuery assumes that the database will take care of updating the version column by either
using an actual VERSION column (if supported by the database in question) or by a trigger or a
similar mechanism.
If you are certain that you do not need optimistic locking but do want to enable write support, you
may point the version column to e.g. a primary key column of the table.
4.2.3. Autocommit mode
SQLContainer is by default in transaction mode, which means that actions that edit, add or remove
items are recorded internally by the container. These actions can be either committed to the database
by calling commit() or discarded by calling rollback().
The container can also be set to auto commit mode. When this mode is enabled, all changes will be
committed to the database immediately. To enable or disable the auto commit mode call the
following method:
public void setAutoCommit(boolean autoCommitEnabled)
It is recommended to leave the auto commit mode disabled, since it ensures that the changes can be
rolled back if any problems are noticed within the container items. Using the auto commit mode
will also lead to failure in item addition if the database table contains non-nullable columns.
4.2.4. Modified state
When used in the transaction mode it may be useful to determine whether the contents of the
SQLContainer have been modified or not. For this purpose the container provides an
isModified() method which will tell the state of the container to the developer. This method
will return true if any items have been added to or removed from the container, as well as if any
value of an existing item has been modified.
Additionally, each RowItem and each ColumnProperty have isModified() methods to allow for a
more detailed view over the modification status. Do note that the modification statuses of RowItems
and ColumnProperties only depend on whether or not the actual Property values have been
modified. That is, they do not reflect situations where the whole RowItem has been marked for
removal or has just been added to the container.
4.3. Caching, paging and refreshing
To decrease the amount of queries made to the database, SQLContainer uses internal caching for
database contents. The caching is implemented with a size-limited LinkedHashMap containing a
mapping from RowIds to RowItems. Typically developers do not need to modify caching options,
8
Vaadin SQLContainer 0.8 User Manual
although some fine-tuning can be done if required.
4.3.1. Container size
The SQLContainer keeps continuously checking the amount of rows in the connected database table
in order to detect external addition or removal of rows. By default, the table row count is assumed
to remain valid for 10 seconds. This value can be altered from code; class SQLContainer, field
sizeValidMilliSeconds.
If the size validity time has expired, the row count will be automatically updated on:
•
•
•
•
•
A call to getItemIds() method
A call to size() method
Some calls to indexOfId(Object itemId) method
A call to firstItemId() method
When the container is fetching a set of rows to the item cache (lazy loading)
4.3.2. Page length and cache size
The page length of the SQLContainer dictates the amount of rows fetched from the database in one
query. The default value is 100, and it can be modified with the setPageLength method. To
avoid constant queries it is recommended to set the page length value to at least 5 times the amount
of rows displayed in a Vaadin Table; obviously this is also dependent on the cache ratio set for the
Table component.
The size of the internal item cache of the SQLContainer is calculated by multiplying the page lenght
with the cache ratio set for the container. The cache ratio can only be set from the code, and the
default value for it is 2. Hence with the default page length of 100 the internal cache size becomes
200 items. This should be enough even for larger Tables while ensuring that no huge amounts of
memory will be used on the cache.
4.3.3. Refreshing the container
Normally the SQLContainer will handle refreshing automatically when required. However there
may be situations where an implicit refresh is needed, e.g. to make sure that the version column is
up-to-date prior to opening the item for editing in a form. For this purpose a refresh() method
is provided. This method simply clears all caches, resets the current item fetching offset and sets the
container size dirty. Any item-related call after this will inevitably result into row count and item
cache update.
Note that a call to the refresh method will not affect or reset the following properties of the
container:
•
•
•
•
•
The QueryDelegate of the container
Autocommit mode
Page length
Filters
Sorting
9
Vaadin SQLContainer 0.8 User Manual
5. Using FreeformQuery and FreeformQueryDelegate
In most cases the provided TableQuery will be enough to allow a developer to gain effortless access
to an SQL data source. However there may arise situations when a more complex query with e.g.
joins is needed. Or perhaps you need to redefine how the writing or filtering should be done. The
FreeformQuery query delegate is provided for this exact purpose. Out of the box the
FreeformQuery supports read-only access to a database, but it can be extended to allow writing
also.
5.1. Getting started
Getting started with the FreeformQuery may be done as shown in the following. The connection
pool initialization is similar to the TableQuery example so it is omitted here. Note that the name(s)
of the primary key column(s) must be provided to the FreeformQuery manually. This is required
because depending on the query the result set may or may not contain data about primary key
columns. In this example there is one primary key column with a name 'ID'.
FreeformQuery query = new FreeformQuery("SELECT * FROM SAMPLE",
connectionPool, "ID");
SQLContainer container = new SQLContainer(query);
5.2. Limitations
While this looks just as easy as with the TableQuery, do note that there are some important caveats
here. Using FreeformQuery like this (without providing FreeformQueryDelegate implementation) it
can only be used as a read-only window to the resultset of the query. Additionally filtering, sorting
and lazy loading features will not be supported, and the row count will be fetched in quite an
inefficient manner. Bearing these limitations in mind, it becomes quite obvious that the developer is
in reality meant to implement the FreeformQueryDelegate interface.
5.3. Creating your own FreeformQueryDelegate
To create your own delegate for FreeformQuery you must implement some or all of the methods
from the FreeformQueryDelegate interface, depending on which ones your use case requires. The
interface contains eight methods which are shown below. For more detailed requirements, see the
JavaDoc documentation of the interface.
/* Read-only queries */
public String getCountQuery()
public String getQueryString(int offset, int limit)
public String getContainsRowQueryString(Object... keys)
/* Filtering and sorting */
public void setFilters(List<Filter> filters)
public void setFilters(List<Filter> filters, FilteringMode filteringMode)
public void setOrderBy(List<OrderBy> orderBys)
/* Write support */
public int storeRow(Connection conn, RowItem row)
public boolean removeRow(Connection conn, RowItem row)
A simple demo implementation of this interface can be found in the SQLContainer package, more
specifically in the class com.vaadin.addon.sqlcontainer.demo.DemoFreeformQueryDelegate.
10
Vaadin SQLContainer 0.8 User Manual
6. Non-implemented methods of Vaadin container interfaces
Due to the database connection inherent to the SQLContainer, some of the methods from the
container interfaces of Vaadin can not (or would not make sense to) be implemented. These
methods are listed below, and they will throw an UnsupportedOperationException on
invocation.
public boolean addContainerProperty(Object propertyId, Class<?> type,
Object defaultValue)
public boolean removeContainerProperty(Object propertyId)
public Item addItem(Object itemId)
public Object addItemAt(int index)
public Item addItemAt(int index, Object newItemId)
public Object addItemAfter(Object previousItemId)
public Item addItemAfter(Object previousItemId, Object newItemId)
Additionally, the following methods of the Item interface are not supported in the RowItem class:
public boolean addItemProperty(Object id, Property property)
public boolean removeItemProperty(Object id)
6.1. About the getItemIds() method
To properly implement the Vaadin Container interface, a getItemIds() method has been
implented in the SQLContainer. By definition this method returns a collection of all the item IDs
present in the container. What this means in the SQLContainer case is that the container has to
query the database for the primary key columns of all the rows present in the connected database
table. It is obvious that this could potentially lead to fetching tens or even hundreds of thousands of
rows in an effort to satisfy the method caller. This will effectively kill the lazy loading properties of
SQLContainer and therefore the following warning is expressed here:
It is highly recommended not to call the getitemIds() method, unless it is known that in the use
case in question the item ID set will always be of reasonable size.
11
Vaadin SQLContainer 0.8 User Manual
7. Appendices
A. Supported databases
The following databases are supported by SQLContainer and TableQuery classes by default:
•
•
•
•
•
HSQLDB [1.8 or newer]
MySQL [5.1 or newer]
PostgreSQL [8.4 or newer]
Oracle Database [10g or newer]
Microsoft SQL Server [2005 or newer]
B. Known issues and limitations of SQLContainer
At this point, there are still some known issues limiting the use of SQLContainer in certain
situations. The issues and brief explanations are listed below:
•
•
•
•
•
•
•
The getItemIds() method is very inefficient - avoid calling it unless absolutely
required!
The addItem() method will return a temporary row ID even if auto commit mode is
enabled.
When using FreeformQuery without providing a FreeformQueryDelegate, the row count
query is very inefficient - avoid using FreeformQuery without implementing at least the
count query properly.
When using FreeformQuery without providing a FreeformQueryDelegate, writing, sorting
and filtering will not be supported..
When using Oracle or MS SQL database, the column name 'rownum' can not be used as a
column name in a table connected to SQLContainer.
• This limitation exists because the databases in question do not support limit/offset
clauses required for paging. Instead, a generated column is used to implement paging
support.
Some SQL data types do not have write support when using TableQuery:
• All binary types
• All custom types
• CLOB (if not converted to a String by the JDBC driver)
When using Oracle database most or all of the numeric types are converted to
java.math.BigDecimal by the Oracle JDBC Driver.
• This seems to be a limitation or a feature of the Oracle Driver and therefore no remedy
to it is provided by the SQLContainer or TableQuery.
C. Planned features
•
•
•
Create a method of easily joining two SQLContainers with TableQueries in cases where e.g.
foreign key is used in one of the tables.
• This would greatly reduce mapping code which currently has to be written to the
applications using the SQLContainer
Provide support for all SQL data types
Provide means for the developer to specify their own column <> Java type mapping
12