Bläddra i källkod

- Merged SQLContainer with Vaadin 6.7.

- Updated EasyMock to version 3.0 (SQLContainer requirement)

svn changeset:20252/svn branch:6.7
tags/6.7.0.beta1
John Alhroos 13 år sedan
förälder
incheckning
111d9c2f9a
59 ändrade filer med 13150 tillägg och 0 borttagningar
  1. 88
    0
      src/com/vaadin/data/util/CacheFlushNotifier.java
  2. 28
    0
      src/com/vaadin/data/util/CacheMap.java
  3. 241
    0
      src/com/vaadin/data/util/ColumnProperty.java
  4. 33
    0
      src/com/vaadin/data/util/OptimisticLockException.java
  5. 28
    0
      src/com/vaadin/data/util/ReadOnlyRowId.java
  6. 53
    0
      src/com/vaadin/data/util/Reference.java
  7. 78
    0
      src/com/vaadin/data/util/RowId.java
  8. 125
    0
      src/com/vaadin/data/util/RowItem.java
  9. 1587
    0
      src/com/vaadin/data/util/SQLContainer.java
  10. 31
    0
      src/com/vaadin/data/util/SQLUtil.java
  11. 29
    0
      src/com/vaadin/data/util/TemporaryRowId.java
  12. 61
    0
      src/com/vaadin/data/util/connection/J2EEConnectionPool.java
  13. 38
    0
      src/com/vaadin/data/util/connection/JDBCConnectionPool.java
  14. 162
    0
      src/com/vaadin/data/util/connection/SimpleJDBCConnectionPool.java
  15. 69
    0
      src/com/vaadin/data/util/filter/Between.java
  16. 78
    0
      src/com/vaadin/data/util/filter/Like.java
  17. 450
    0
      src/com/vaadin/data/util/query/FreeformQuery.java
  18. 115
    0
      src/com/vaadin/data/util/query/FreeformQueryDelegate.java
  19. 54
    0
      src/com/vaadin/data/util/query/FreeformStatementDelegate.java
  20. 43
    0
      src/com/vaadin/data/util/query/OrderBy.java
  21. 208
    0
      src/com/vaadin/data/util/query/QueryDelegate.java
  22. 707
    0
      src/com/vaadin/data/util/query/TableQuery.java
  23. 308
    0
      src/com/vaadin/data/util/query/generator/DefaultSQLGenerator.java
  24. 101
    0
      src/com/vaadin/data/util/query/generator/MSSQLGenerator.java
  25. 99
    0
      src/com/vaadin/data/util/query/generator/OracleGenerator.java
  26. 85
    0
      src/com/vaadin/data/util/query/generator/SQLGenerator.java
  27. 131
    0
      src/com/vaadin/data/util/query/generator/StatementHelper.java
  28. 18
    0
      src/com/vaadin/data/util/query/generator/filter/AndTranslator.java
  29. 21
    0
      src/com/vaadin/data/util/query/generator/filter/BetweenTranslator.java
  30. 33
    0
      src/com/vaadin/data/util/query/generator/filter/CompareTranslator.java
  31. 11
    0
      src/com/vaadin/data/util/query/generator/filter/FilterTranslator.java
  32. 17
    0
      src/com/vaadin/data/util/query/generator/filter/IsNullTranslator.java
  33. 27
    0
      src/com/vaadin/data/util/query/generator/filter/LikeTranslator.java
  34. 26
    0
      src/com/vaadin/data/util/query/generator/filter/NotTranslator.java
  35. 18
    0
      src/com/vaadin/data/util/query/generator/filter/OrTranslator.java
  36. 94
    0
      src/com/vaadin/data/util/query/generator/filter/QueryBuilder.java
  37. 25
    0
      src/com/vaadin/data/util/query/generator/filter/SimpleStringTranslator.java
  38. 53
    0
      src/com/vaadin/data/util/query/generator/filter/StringDecorator.java
  39. 191
    0
      tests/src/com/vaadin/tests/containers/sqlcontainer/CheckboxUpdateProblem.java
  40. 134
    0
      tests/src/com/vaadin/tests/containers/sqlcontainer/MassInsertMemoryLeakTestApp.java
  41. 146
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/AllTests.java
  42. 177
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/ColumnPropertyTest.java
  43. 132
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/DataGenerator.java
  44. 66
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/FreeformQueryUtil.java
  45. 50
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/ReadOnlyRowIdTest.java
  46. 55
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/RowIdTest.java
  47. 1501
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/SQLContainerTableQueryTest.java
  48. 2400
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/SQLContainerTest.java
  49. 157
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/TicketTests.java
  50. 52
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/UtilTest.java
  51. 101
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/J2EEConnectionPoolTest.java
  52. 24
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/MockInitialContextFactory.java
  53. 172
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java
  54. 122
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/filters/BetweenTest.java
  55. 229
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/filters/LikeTest.java
  56. 241
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/generator/SQLGeneratorsTest.java
  57. 897
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/query/FreeformQueryTest.java
  58. 311
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/query/QueryBuilderTest.java
  59. 619
    0
      tests/src/com/vaadin/tests/server/container/sqlcontainer/query/TableQueryTest.java

+ 88
- 0
src/com/vaadin/data/util/CacheFlushNotifier.java Visa fil

@@ -0,0 +1,88 @@
package com.vaadin.data.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import com.vaadin.data.util.query.FreeformQuery;
import com.vaadin.data.util.query.QueryDelegate;
import com.vaadin.data.util.query.TableQuery;
/**
* CacheFlushNotifier is a simple static notification mechanism to inform other
* SQLContainers that the contents of their caches may have become stale.
*/
class CacheFlushNotifier {
/*
* SQLContainer instance reference list and dead reference queue. Used for
* the cache flush notification feature.
*/
private static List<WeakReference<SQLContainer>> allInstances = new ArrayList<WeakReference<SQLContainer>>();
private static ReferenceQueue<SQLContainer> deadInstances = new ReferenceQueue<SQLContainer>();
/**
* Adds the given SQLContainer to the cache flush notification receiver list
*
* @param c
* Container to add
*/
public static void addInstance(SQLContainer c) {
removeDeadReferences();
if (c != null) {
allInstances.add(new WeakReference<SQLContainer>(c, deadInstances));
}
}
/**
* Removes dead references from instance list
*/
private static void removeDeadReferences() {
java.lang.ref.Reference<? extends SQLContainer> dead = deadInstances
.poll();
while (dead != null) {
allInstances.remove(dead);
dead = deadInstances.poll();
}
}
/**
* Iterates through the instances and notifies containers which are
* connected to the same table or are using the same query string.
*
* @param c
* SQLContainer that issued the cache flush notification
*/
public static void notifyOfCacheFlush(SQLContainer c) {
removeDeadReferences();
for (WeakReference<SQLContainer> wr : allInstances) {
if (wr.get() != null) {
SQLContainer wrc = wr.get();
if (wrc == null) {
continue;
}
/*
* If the reference points to the container sending the
* notification, do nothing.
*/
if (wrc.equals(c)) {
continue;
}
/* Compare QueryDelegate types and tableName/queryString */
QueryDelegate wrQd = wrc.getQueryDelegate();
QueryDelegate qd = c.getQueryDelegate();
if (wrQd instanceof TableQuery
&& qd instanceof TableQuery
&& ((TableQuery) wrQd).getTableName().equals(
((TableQuery) qd).getTableName())) {
wrc.refresh();
} else if (wrQd instanceof FreeformQuery
&& qd instanceof FreeformQuery
&& ((FreeformQuery) wrQd).getQueryString().equals(
((FreeformQuery) qd).getQueryString())) {
wrc.refresh();
}
}
}
}
}

+ 28
- 0
src/com/vaadin/data/util/CacheMap.java Visa fil

@@ -0,0 +1,28 @@
package com.vaadin.data.util;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* CacheMap extends LinkedHashMap, adding the possibility to adjust maximum
* number of items. In SQLContainer this is used for RowItem -cache. Cache size
* will be two times the page length parameter of the container.
*/
class CacheMap<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 679999766473555231L;
private int cacheLimit = SQLContainer.CACHE_RATIO
* SQLContainer.DEFAULT_PAGE_LENGTH;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > cacheLimit;
}
void setCacheLimit(int limit) {
cacheLimit = limit > 0 ? limit : SQLContainer.DEFAULT_PAGE_LENGTH;
}
int getCacheLimit() {
return cacheLimit;
}
}

+ 241
- 0
src/com/vaadin/data/util/ColumnProperty.java Visa fil

@@ -0,0 +1,241 @@
package com.vaadin.data.util;

import java.lang.reflect.Constructor;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;

import com.vaadin.data.Property;

/**
* ColumnProperty represents the value of one column in a RowItem. In addition
* to the value, ColumnProperty also contains some basic column attributes such
* as nullability status, read-only status and data type.
*
* Note that depending on the QueryDelegate in use this does not necessarily map
* into an actual column in a database table.
*/
final public class ColumnProperty implements Property {
private static final long serialVersionUID = -3694463129581802457L;

private RowItem owner;

private String propertyId;

private boolean readOnly;
private boolean allowReadOnlyChange = true;
private boolean nullable = true;

private Object value;
private Object changedValue;
private Class<?> type;

private boolean modified;

private boolean versionColumn;

/**
* Prevent instantiation without required parameters.
*/
@SuppressWarnings("unused")
private ColumnProperty() {
}

public ColumnProperty(String propertyId, boolean readOnly,
boolean allowReadOnlyChange, boolean nullable, Object value,
Class<?> type) {
if (propertyId == null) {
throw new IllegalArgumentException("Properties must be named.");
}
if (type == null) {
throw new IllegalArgumentException("Property type must be set.");
}
this.propertyId = propertyId;
this.type = type;
this.value = value;

this.allowReadOnlyChange = allowReadOnlyChange;
this.nullable = nullable;
this.readOnly = readOnly;
}

public Object getValue() {
if (isModified()) {
return changedValue;
}
return value;
}

public void setValue(Object newValue) throws ReadOnlyException,
ConversionException {
if (newValue == null && !nullable) {
throw new NotNullableException(
"Null values are not allowed for this property.");
}
if (readOnly) {
throw new ReadOnlyException(
"Cannot set value for read-only property.");
}

/* Check if this property is a date property. */
boolean isDateProperty = Time.class.equals(getType())
|| Date.class.equals(getType())
|| Timestamp.class.equals(getType());

if (newValue != null) {
/* Handle SQL dates, times and Timestamps given as java.util.Date */
if (isDateProperty) {
/*
* Try to get the millisecond value from the new value of this
* property. Possible type to convert from is java.util.Date.
*/
long millis = 0;
if (newValue instanceof java.util.Date) {
millis = ((java.util.Date) newValue).getTime();
/*
* Create the new object based on the millisecond value,
* according to the type of this property.
*/
if (Time.class.equals(getType())) {
newValue = new Time(millis);
} else if (Date.class.equals(getType())) {
newValue = new Date(millis);
} else if (Timestamp.class.equals(getType())) {
newValue = new Timestamp(millis);
}
}
}

/*
* If the type is not correct, try to generate it through a possibly
* existing String constructor.
*/
if (!getType().isAssignableFrom(newValue.getClass())) {
try {
final Constructor<?> constr = getType().getConstructor(
new Class[] { String.class });
newValue = constr.newInstance(new Object[] { newValue
.toString() });
} catch (Exception e) {
throw new ConversionException(e);
}
}

/*
* If the value to be set is the same that has already been set, do
* not set it again.
*/
if (newValue.equals(value)) {
return;
}
}

/* Set the new value and notify container of the change. */
changedValue = newValue;
owner.getContainer().itemChangeNotification(owner);
modified = true;
}

public Class<?> getType() {
return type;
}

public boolean isReadOnly() {
return readOnly;
}

public boolean isReadOnlyChangeAllowed() {
return allowReadOnlyChange;
}

public void setReadOnly(boolean newStatus) {
if (allowReadOnlyChange) {
readOnly = newStatus;
}
}

public String getPropertyId() {
return propertyId;
}

@Override
public String toString() {
Object val = getValue();
if (val == null) {
return null;
}
return val.toString();
}

public void setOwner(RowItem owner) {
if (owner == null) {
throw new IllegalArgumentException("Owner can not be set to null.");
}
if (this.owner != null) {
throw new IllegalStateException(
"ColumnProperties can only be bound once.");
}
this.owner = owner;
}

public boolean isModified() {
return modified;
}

public boolean isVersionColumn() {
return versionColumn;
}

public void setVersionColumn(boolean versionColumn) {
this.versionColumn = versionColumn;
}

public boolean isNullable() {
return nullable;
}

/**
* An exception that signals that a <code>null</code> value was passed to
* the <code>setValue</code> method, but the value of this property can not
* be set to <code>null</code>.
*/
@SuppressWarnings("serial")
public class NotNullableException extends RuntimeException {

/**
* Constructs a new <code>NotNullableException</code> without a detail
* message.
*/
public NotNullableException() {
}

/**
* Constructs a new <code>NotNullableException</code> with the specified
* detail message.
*
* @param msg
* the detail message
*/
public NotNullableException(String msg) {
super(msg);
}

/**
* Constructs a new <code>NotNullableException</code> from another
* exception.
*
* @param cause
* The cause of the failure
*/
public NotNullableException(Throwable cause) {
super(cause);
}
}

public void commit() {
if (isModified()) {
modified = false;
value = changedValue;
}
}
}

+ 33
- 0
src/com/vaadin/data/util/OptimisticLockException.java Visa fil

@@ -0,0 +1,33 @@
package com.vaadin.data.util;

/**
* An OptimisticLockException is thrown when trying to update or delete a row
* that has been changed since last read from the database.
*
* OptimisticLockException is a runtime exception because optimistic locking is
* turned off by default, and as such will never be thrown in a default
* configuration. In order to turn on optimistic locking, you need to specify
* the version column in your TableQuery instance.
*
* @see com.vaadin.addon.sqlcontainer.query.TableQuery#setVersionColumn(String)
*
* @author Jonatan Kronqvist / Vaadin Ltd
*/
public class OptimisticLockException extends RuntimeException {

private final RowId rowId;

public OptimisticLockException(RowId rowId) {
super();
this.rowId = rowId;
}

public OptimisticLockException(String msg, RowId rowId) {
super(msg);
this.rowId = rowId;
}

public RowId getRowId() {
return rowId;
}
}

+ 28
- 0
src/com/vaadin/data/util/ReadOnlyRowId.java Visa fil

@@ -0,0 +1,28 @@
package com.vaadin.data.util;

public class ReadOnlyRowId extends RowId {
private static final long serialVersionUID = -2626764781642012467L;
private final Integer rowNum;

public ReadOnlyRowId(int rowNum) {
super();
this.rowNum = rowNum;
}

@Override
public int hashCode() {
return rowNum.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof ReadOnlyRowId)) {
return false;
}
return rowNum.equals(((ReadOnlyRowId) obj).rowNum);
}

public int getRowNum() {
return rowNum;
}
}

+ 53
- 0
src/com/vaadin/data/util/Reference.java Visa fil

@@ -0,0 +1,53 @@
package com.vaadin.data.util;
import java.io.Serializable;
/**
* The reference class represents a simple [usually foreign key] reference to
* another SQLContainer. Actual foreign key reference in the database is not
* required, but it is recommended to make sure that certain constraints are
* followed.
*/
@SuppressWarnings("serial")
class Reference implements Serializable {
/**
* The SQLContainer that this reference points to.
*/
private SQLContainer referencedContainer;
/**
* The column ID/name in the referencing SQLContainer that contains the key
* used for the reference.
*/
private String referencingColumn;
/**
* The column ID/name in the referenced SQLContainer that contains the key
* used for the reference.
*/
private String referencedColumn;
/**
* Constructs a new reference to be used within the SQLContainer to
* reference another SQLContainer.
*/
Reference(SQLContainer referencedContainer, String referencingColumn,
String referencedColumn) {
this.referencedContainer = referencedContainer;
this.referencingColumn = referencingColumn;
this.referencedColumn = referencedColumn;
}
SQLContainer getReferencedContainer() {
return referencedContainer;
}
String getReferencingColumn() {
return referencingColumn;
}
String getReferencedColumn() {
return referencedColumn;
}
}

+ 78
- 0
src/com/vaadin/data/util/RowId.java Visa fil

@@ -0,0 +1,78 @@
package com.vaadin.data.util;
import java.io.Serializable;
/**
* RowId represents identifiers of a single database result set row.
*
* The data structure of a RowId is an Object array which contains the values of
* the primary key columns of the identified row. This allows easy equals()
* -comparison of RowItems.
*/
public class RowId implements Serializable {
private static final long serialVersionUID = -3161778404698901258L;
protected Object[] id;
/**
* Prevent instantiation without required parameters.
*/
protected RowId() {
}
public RowId(Object[] id) {
if (id == null) {
throw new IllegalArgumentException("id parameter must not be null!");
}
this.id = id;
}
public Object[] getId() {
return id;
}
@Override
public int hashCode() {
int result = 31;
if (id != null) {
for (Object o : id) {
if (o != null) {
result += o.hashCode();
}
}
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof RowId)) {
return false;
}
Object[] compId = ((RowId) obj).getId();
if (id == null && compId == null) {
return true;
}
if (id.length != compId.length) {
return false;
}
for (int i = 0; i < id.length; i++) {
if ((id[i] == null && compId[i] != null)
|| (id[i] != null && !id[i].equals(compId[i]))) {
return false;
}
}
return true;
}
@Override
public String toString() {
StringBuffer s = new StringBuffer();
for (int i = 0; i < id.length; i++) {
s.append(id[i]);
if (i < id.length - 1) {
s.append("/");
}
}
return s.toString();
}
}

+ 125
- 0
src/com/vaadin/data/util/RowItem.java Visa fil

@@ -0,0 +1,125 @@
package com.vaadin.data.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import com.vaadin.data.Item;
import com.vaadin.data.Property;

/**
* RowItem represents one row of a result set obtained from a QueryDelegate.
*
* Note that depending on the QueryDelegate in use this does not necessarily map
* into an actual row in a database table.
*/
public final class RowItem implements Item {
private static final long serialVersionUID = -6228966439127951408L;
private SQLContainer container;
private RowId id;
private Collection<ColumnProperty> properties;

/**
* Prevent instantiation without required parameters.
*/
@SuppressWarnings("unused")
private RowItem() {
}

public RowItem(SQLContainer container, RowId id,
Collection<ColumnProperty> properties) {
if (container == null) {
throw new IllegalArgumentException("Container cannot be null.");
}
if (id == null) {
throw new IllegalArgumentException("Row ID cannot be null.");
}
this.container = container;
this.properties = properties;
/* Set this RowItem as owner to the properties */
if (properties != null) {
for (ColumnProperty p : properties) {
p.setOwner(this);
}
}
this.id = id;
}

public Property getItemProperty(Object id) {
if (id instanceof String && id != null) {
for (ColumnProperty cp : properties) {
if (id.equals(cp.getPropertyId())) {
return cp;
}
}
}
return null;
}

public Collection<?> getItemPropertyIds() {
Collection<String> ids = new ArrayList<String>(properties.size());
for (ColumnProperty cp : properties) {
ids.add(cp.getPropertyId());
}
return Collections.unmodifiableCollection(ids);
}

/**
* Adding properties is not supported. Properties are generated by
* SQLContainer.
*/
public boolean addItemProperty(Object id, Property property)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}

/**
* Removing properties is not supported. Properties are generated by
* SQLContainer.
*/
public boolean removeItemProperty(Object id)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}

public RowId getId() {
return id;
}

public SQLContainer getContainer() {
return container;
}

public boolean isModified() {
if (properties != null) {
for (ColumnProperty p : properties) {
if (p.isModified()) {
return true;
}
}
}
return false;
}

@Override
public String toString() {
StringBuffer s = new StringBuffer();
s.append("ID:");
s.append(getId().toString());
for (Object propId : getItemPropertyIds()) {
s.append("|");
s.append(propId.toString());
s.append(":");
s.append(getItemProperty(propId).toString());
}
return s.toString();
}

public void commit() {
if (properties != null) {
for (ColumnProperty p : properties) {
p.commit();
}
}
}
}

+ 1587
- 0
src/com/vaadin/data/util/SQLContainer.java
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 31
- 0
src/com/vaadin/data/util/SQLUtil.java Visa fil

@@ -0,0 +1,31 @@
package com.vaadin.data.util;

public class SQLUtil {
/**
* Escapes different special characters in strings that are passed to SQL.
* Replaces the following:
*
* <list> <li>' is replaced with ''</li> <li>\x00 is removed</li> <li>\ is
* replaced with \\</li> <li>" is replaced with \"</li> <li>
* \x1a is removed</li> </list>
*
* Also note! The escaping done here may or may not be enough to prevent any
* and all SQL injections so it is recommended to check user input before
* giving it to the SQLContainer/TableQuery.
*
* @param constant
* @return \\\'\'
*/
public static String escapeSQL(String constant) {
if (constant == null) {
return null;
}
String fixedConstant = constant;
fixedConstant = fixedConstant.replaceAll("\\\\x00", "");
fixedConstant = fixedConstant.replaceAll("\\\\x1a", "");
fixedConstant = fixedConstant.replaceAll("'", "''");
fixedConstant = fixedConstant.replaceAll("\\\\", "\\\\\\\\");
fixedConstant = fixedConstant.replaceAll("\\\"", "\\\\\"");
return fixedConstant;
}
}

+ 29
- 0
src/com/vaadin/data/util/TemporaryRowId.java Visa fil

@@ -0,0 +1,29 @@
package com.vaadin.data.util;

public class TemporaryRowId extends RowId {
private static final long serialVersionUID = -641983830469018329L;

public TemporaryRowId(Object[] id) {
super(id);
}

@Override
public int hashCode() {
return id.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof TemporaryRowId)) {
return false;
}
Object[] compId = ((TemporaryRowId) obj).getId();
return id.equals(compId);
}

@Override
public String toString() {
return "Temporary row id";
}

}

+ 61
- 0
src/com/vaadin/data/util/connection/J2EEConnectionPool.java Visa fil

@@ -0,0 +1,61 @@
package com.vaadin.data.util.connection;

import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class J2EEConnectionPool implements JDBCConnectionPool {

private String dataSourceJndiName;

private DataSource dataSource = null;

public J2EEConnectionPool(DataSource dataSource) {
this.dataSource = dataSource;
}

public J2EEConnectionPool(String dataSourceJndiName) {
this.dataSourceJndiName = dataSourceJndiName;
}

public Connection reserveConnection() throws SQLException {
Connection conn = getDataSource().getConnection();
conn.setAutoCommit(false);

return conn;
}

private DataSource getDataSource() throws SQLException {
if (dataSource == null) {
dataSource = lookupDataSource();
}
return dataSource;
}

private DataSource lookupDataSource() throws SQLException {
try {
InitialContext ic = new InitialContext();
return (DataSource) ic.lookup(dataSourceJndiName);
} catch (NamingException e) {
throw new SQLException(
"NamingException - Cannot connect to the database. Cause: "
+ e.getMessage());
}
}

public void releaseConnection(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

public void destroy() {
dataSource = null;
}

}

+ 38
- 0
src/com/vaadin/data/util/connection/JDBCConnectionPool.java Visa fil

@@ -0,0 +1,38 @@
package com.vaadin.data.util.connection;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;

/**
* Interface for implementing connection pools to be used with SQLContainer.
*/
public interface JDBCConnectionPool extends Serializable {
/**
* Retrieves a connection.
*
* @return a usable connection to the database
* @throws SQLException
*/
public Connection reserveConnection() throws SQLException;

/**
* Releases a connection that was retrieved earlier.
*
* Note that depending on implementation, the transaction possibly open in
* the connection may or may not be rolled back.
*
* @param conn
* Connection to be released
*/
public void releaseConnection(Connection conn);

/**
* Destroys the connection pool: close() is called an all the connections in
* the pool, whether available or reserved.
*
* This method was added to fix PostgreSQL -related issues with connections
* that were left hanging 'idle'.
*/
public void destroy();
}

+ 162
- 0
src/com/vaadin/data/util/connection/SimpleJDBCConnectionPool.java Visa fil

@@ -0,0 +1,162 @@
package com.vaadin.data.util.connection;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
/**
* Simple implementation of the JDBCConnectionPool interface. Handles loading
* the JDBC driver, setting up the connections and ensuring they are still
* usable upon release.
*/
@SuppressWarnings("serial")
public class SimpleJDBCConnectionPool implements JDBCConnectionPool {
private int initialConnections = 5;
private int maxConnections = 20;
private String driverName;
private String connectionUri;
private String userName;
private String password;
private transient Set<Connection> availableConnections;
private transient Set<Connection> reservedConnections;
private boolean initialized;
public SimpleJDBCConnectionPool(String driverName, String connectionUri,
String userName, String password) throws SQLException {
if (driverName == null) {
throw new IllegalArgumentException(
"JDBC driver class name must be given.");
}
if (connectionUri == null) {
throw new IllegalArgumentException(
"Database connection URI must be given.");
}
if (userName == null) {
throw new IllegalArgumentException(
"Database username must be given.");
}
if (password == null) {
throw new IllegalArgumentException(
"Database password must be given.");
}
this.driverName = driverName;
this.connectionUri = connectionUri;
this.userName = userName;
this.password = password;
/* Initialize JDBC driver */
try {
Class.forName(driverName).newInstance();
} catch (Exception ex) {
throw new RuntimeException("Specified JDBC Driver: " + driverName
+ " - initialization failed.", ex);
}
}
public SimpleJDBCConnectionPool(String driverName, String connectionUri,
String userName, String password, int initialConnections,
int maxConnections) throws SQLException {
this(driverName, connectionUri, userName, password);
this.initialConnections = initialConnections;
this.maxConnections = maxConnections;
}
private void initializeConnections() throws SQLException {
availableConnections = new HashSet<Connection>(initialConnections);
reservedConnections = new HashSet<Connection>(initialConnections);
for (int i = 0; i < initialConnections; i++) {
availableConnections.add(createConnection());
}
initialized = true;
}
public synchronized Connection reserveConnection() throws SQLException {
if (!initialized) {
initializeConnections();
}
if (availableConnections.isEmpty()) {
if (reservedConnections.size() < maxConnections) {
availableConnections.add(createConnection());
} else {
throw new SQLException("Connection limit has been reached.");
}
}
Connection c = availableConnections.iterator().next();
availableConnections.remove(c);
reservedConnections.add(c);
return c;
}
public synchronized void releaseConnection(Connection conn) {
if (conn == null || !initialized) {
return;
}
/* Try to roll back if necessary */
try {
if (!conn.getAutoCommit()) {
conn.rollback();
}
} catch (SQLException e) {
/* Roll back failed, close and discard connection */
try {
conn.close();
} catch (SQLException e1) {
/* Nothing needs to be done */
}
reservedConnections.remove(conn);
return;
}
reservedConnections.remove(conn);
availableConnections.add(conn);
}
private Connection createConnection() throws SQLException {
Connection c = DriverManager.getConnection(connectionUri, userName,
password);
c.setAutoCommit(false);
if (driverName.toLowerCase().contains("mysql")) {
try {
Statement s = c.createStatement();
s.execute("SET SESSION sql_mode = 'ANSI'");
s.close();
} catch (Exception e) {
// Failed to set ansi mode; continue
}
}
return c;
}
public void destroy() {
for (Connection c : availableConnections) {
try {
c.close();
} catch (SQLException e) {
// No need to do anything
}
}
for (Connection c : reservedConnections) {
try {
c.close();
} catch (SQLException e) {
// No need to do anything
}
}
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
initialized = false;
out.defaultWriteObject();
}
}

+ 69
- 0
src/com/vaadin/data/util/filter/Between.java Visa fil

@@ -0,0 +1,69 @@
package com.vaadin.data.util.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.Item;

public class Between implements Filter {

private final Object propertyId;
private final Comparable startValue;
private final Comparable endValue;

public Between(Object propertyId, Comparable startValue, Comparable endValue) {
this.propertyId = propertyId;
this.startValue = startValue;
this.endValue = endValue;
}

public Object getPropertyId() {
return propertyId;
}

public Comparable<?> getStartValue() {
return startValue;
}

public Comparable<?> getEndValue() {
return endValue;
}

public boolean passesFilter(Object itemId, Item item)
throws UnsupportedOperationException {
Object value = item.getItemProperty(getPropertyId()).getValue();
if (value instanceof Comparable) {
Comparable cval = (Comparable) value;
return cval.compareTo(getStartValue()) >= 0
&& cval.compareTo(getEndValue()) <= 0;
}
return false;
}

public boolean appliesToProperty(Object propertyId) {
return getPropertyId() != null && getPropertyId().equals(propertyId);
}

@Override
public int hashCode() {
return getPropertyId().hashCode() + getStartValue().hashCode()
+ getEndValue().hashCode();
}

@Override
public boolean equals(Object obj) {
// Only objects of the same class can be equal
if (!getClass().equals(obj.getClass())) {
return false;
}
final Between o = (Between) obj;

// Checks the properties one by one
boolean propertyIdEqual = (null != getPropertyId()) ? getPropertyId()
.equals(o.getPropertyId()) : null == o.getPropertyId();
boolean startValueEqual = (null != getStartValue()) ? getStartValue()
.equals(o.getStartValue()) : null == o.getStartValue();
boolean endValueEqual = (null != getEndValue()) ? getEndValue().equals(
o.getEndValue()) : null == o.getEndValue();
return propertyIdEqual && startValueEqual && endValueEqual;

}
}

+ 78
- 0
src/com/vaadin/data/util/filter/Like.java Visa fil

@@ -0,0 +1,78 @@
package com.vaadin.data.util.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.Item;

public class Like implements Filter {
private final Object propertyId;
private final String value;
private boolean caseSensitive;

public Like(String propertyId, String value) {
this(propertyId, value, true);
}

public Like(String propertyId, String value, boolean caseSensitive) {
this.propertyId = propertyId;
this.value = value;
setCaseSensitive(caseSensitive);
}

public Object getPropertyId() {
return propertyId;
}

public String getValue() {
return value;
}

public void setCaseSensitive(boolean caseSensitive) {
this.caseSensitive = caseSensitive;
}

public boolean isCaseSensitive() {
return caseSensitive;
}

public boolean passesFilter(Object itemId, Item item)
throws UnsupportedOperationException {
if (!item.getItemProperty(getPropertyId()).getType()
.isAssignableFrom(String.class)) {
// We can only handle strings
return false;
}
String colValue = (String) item.getItemProperty(getPropertyId())
.getValue();

String pattern = getValue().replace("%", ".*");
if (isCaseSensitive()) {
return colValue.matches(pattern);
}
return colValue.toUpperCase().matches(pattern.toUpperCase());
}

public boolean appliesToProperty(Object propertyId) {
return getPropertyId() != null && getPropertyId().equals(propertyId);
}

@Override
public int hashCode() {
return getPropertyId().hashCode() + getValue().hashCode();
}

@Override
public boolean equals(Object obj) {
// Only objects of the same class can be equal
if (!getClass().equals(obj.getClass())) {
return false;
}
final Like o = (Like) obj;

// Checks the properties one by one
boolean propertyIdEqual = (null != getPropertyId()) ? getPropertyId()
.equals(o.getPropertyId()) : null == o.getPropertyId();
boolean valueEqual = (null != getValue()) ? getValue().equals(
o.getValue()) : null == o.getValue();
return propertyIdEqual && valueEqual;
}
}

+ 450
- 0
src/com/vaadin/data/util/query/FreeformQuery.java Visa fil

@@ -0,0 +1,450 @@
package com.vaadin.data.util.query;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.SQLContainer;
import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.query.generator.StatementHelper;
import com.vaadin.data.util.query.generator.filter.QueryBuilder;

@SuppressWarnings("serial")
public class FreeformQuery implements QueryDelegate {

FreeformQueryDelegate delegate = null;
private String queryString;
private List<String> primaryKeyColumns;
private JDBCConnectionPool connectionPool;
private transient Connection activeConnection = null;

/**
* Prevent no-parameters instantiation of FreeformQuery
*/
@SuppressWarnings("unused")
private FreeformQuery() {
}

/**
* Creates a new freeform query delegate to be used with the
* {@link SQLContainer}.
*
* @param queryString
* The actual query to perform.
* @param primaryKeyColumns
* The primary key columns. Read-only mode is forced if this
* parameter is null or empty.
* @param connectionPool
* the JDBCConnectionPool to use to open connections to the SQL
* database.
* @deprecated @see
* {@link FreeformQuery#FreeformQuery(String, JDBCConnectionPool, String...)}
*/
@Deprecated
public FreeformQuery(String queryString, List<String> primaryKeyColumns,
JDBCConnectionPool connectionPool) {
if (primaryKeyColumns == null) {
primaryKeyColumns = new ArrayList<String>();
}
if (primaryKeyColumns.contains("")) {
throw new IllegalArgumentException(
"The primary key columns contain an empty string!");
} else if (queryString == null || "".equals(queryString)) {
throw new IllegalArgumentException(
"The query string may not be empty or null!");
} else if (connectionPool == null) {
throw new IllegalArgumentException(
"The connectionPool may not be null!");
}
this.queryString = queryString;
this.primaryKeyColumns = Collections
.unmodifiableList(primaryKeyColumns);
this.connectionPool = connectionPool;
}

/**
* Creates a new freeform query delegate to be used with the
* {@link SQLContainer}.
*
* @param queryString
* The actual query to perform.
* @param connectionPool
* the JDBCConnectionPool to use to open connections to the SQL
* database.
* @param primaryKeyColumns
* The primary key columns. Read-only mode is forced if none are
* provided. (optional)
*/
public FreeformQuery(String queryString, JDBCConnectionPool connectionPool,
String... primaryKeyColumns) {
this(queryString, Arrays.asList(primaryKeyColumns), connectionPool);
}

/**
* This implementation of getCount() actually fetches all records from the
* database, which might be a performance issue. Override this method with a
* SELECT COUNT(*) ... query if this is too slow for your needs.
*
* {@inheritDoc}
*/
public int getCount() throws SQLException {
// First try the delegate
int count = countByDelegate();
if (count < 0) {
// Couldn't use the delegate, use the bad way.
Connection conn = getConnection();
Statement statement = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);

ResultSet rs = statement.executeQuery(queryString);
if (rs.last()) {
count = rs.getRow();
} else {
count = 0;
}
rs.close();
statement.close();
releaseConnection(conn);
}
return count;
}

@SuppressWarnings("deprecation")
private int countByDelegate() throws SQLException {
int count = -1;
if (delegate == null) {
return count;
}
/* First try using prepared statement */
if (delegate instanceof FreeformStatementDelegate) {
try {
StatementHelper sh = ((FreeformStatementDelegate) delegate)
.getCountStatement();
Connection c = getConnection();
PreparedStatement pstmt = c.prepareStatement(sh
.getQueryString());
sh.setParameterValuesToStatement(pstmt);
ResultSet rs = pstmt.executeQuery();
rs.next();
count = rs.getInt(1);
rs.close();
pstmt.clearParameters();
pstmt.close();
releaseConnection(c);
return count;
} catch (UnsupportedOperationException e) {
// Count statement generation not supported
}
}
/* Try using regular statement */
try {
String countQuery = delegate.getCountQuery();
if (countQuery != null) {
Connection conn = getConnection();
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery(countQuery);
rs.next();
count = rs.getInt(1);
rs.close();
statement.close();
releaseConnection(conn);
return count;
}
} catch (UnsupportedOperationException e) {
// Count query generation not supported
}
return count;
}

private Connection getConnection() throws SQLException {
if (activeConnection != null) {
return activeConnection;
}
return connectionPool.reserveConnection();
}

/**
* Fetches the results for the query. This implementation always fetches the
* entire record set, ignoring the offset and pagelength parameters. In
* order to support lazy loading of records, you must supply a
* FreeformQueryDelegate that implements the
* FreeformQueryDelegate.getQueryString(int,int) method.
*
* @throws SQLException
*
* @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getQueryString(int,
* int) {@inheritDoc}
*/
@SuppressWarnings("deprecation")
public ResultSet getResults(int offset, int pagelength) throws SQLException {
if (activeConnection == null) {
throw new SQLException("No active transaction!");
}
String query = queryString;
if (delegate != null) {
/* First try using prepared statement */
if (delegate instanceof FreeformStatementDelegate) {
try {
StatementHelper sh = ((FreeformStatementDelegate) delegate)
.getQueryStatement(offset, pagelength);
PreparedStatement pstmt = activeConnection
.prepareStatement(sh.getQueryString());
sh.setParameterValuesToStatement(pstmt);
return pstmt.executeQuery();
} catch (UnsupportedOperationException e) {
// Statement generation not supported, continue...
}
}
try {
query = delegate.getQueryString(offset, pagelength);
} catch (UnsupportedOperationException e) {
// This is fine, we'll just use the default queryString.
}
}
Statement statement = activeConnection.createStatement();
ResultSet rs = statement.executeQuery(query);
return rs;
}

@SuppressWarnings("deprecation")
public boolean implementationRespectsPagingLimits() {
if (delegate == null) {
return false;
}
/* First try using prepared statement */
if (delegate instanceof FreeformStatementDelegate) {
try {
StatementHelper sh = ((FreeformStatementDelegate) delegate)
.getCountStatement();
if (sh != null && sh.getQueryString() != null
&& sh.getQueryString().length() > 0) {
return true;
}
} catch (UnsupportedOperationException e) {
// Statement generation not supported, continue...
}
}
try {
String queryString = delegate.getQueryString(0, 50);
return queryString != null && queryString.length() > 0;
} catch (UnsupportedOperationException e) {
return false;
}
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util
* .List)
*/
public void setFilters(List<Filter> filters)
throws UnsupportedOperationException {
if (delegate != null) {
delegate.setFilters(filters);
} else if (filters != null) {
throw new UnsupportedOperationException(
"FreeFormQueryDelegate not set!");
}
}

public void setOrderBy(List<OrderBy> orderBys)
throws UnsupportedOperationException {
if (delegate != null) {
delegate.setOrderBy(orderBys);
} else if (orderBys != null) {
throw new UnsupportedOperationException(
"FreeFormQueryDelegate not set!");
}
}

public int storeRow(RowItem row) throws SQLException {
if (activeConnection == null) {
throw new IllegalStateException("No transaction is active!");
} else if (primaryKeyColumns.isEmpty()) {
throw new UnsupportedOperationException(
"Cannot store items fetched with a read-only freeform query!");
}
if (delegate != null) {
return delegate.storeRow(activeConnection, row);
} else {
throw new UnsupportedOperationException(
"FreeFormQueryDelegate not set!");
}
}

public boolean removeRow(RowItem row) throws SQLException {
if (activeConnection == null) {
throw new IllegalStateException("No transaction is active!");
} else if (primaryKeyColumns.isEmpty()) {
throw new UnsupportedOperationException(
"Cannot remove items fetched with a read-only freeform query!");
}
if (delegate != null) {
return delegate.removeRow(activeConnection, row);
} else {
throw new UnsupportedOperationException(
"FreeFormQueryDelegate not set!");
}
}

public synchronized void beginTransaction()
throws UnsupportedOperationException, SQLException {
if (activeConnection != null) {
throw new IllegalStateException("A transaction is already active!");
}
activeConnection = connectionPool.reserveConnection();
activeConnection.setAutoCommit(false);
}

public synchronized void commit() throws UnsupportedOperationException,
SQLException {
if (activeConnection == null) {
throw new SQLException("No active transaction");
}
if (!activeConnection.getAutoCommit()) {
activeConnection.commit();
}
connectionPool.releaseConnection(activeConnection);
activeConnection = null;
}

public synchronized void rollback() throws UnsupportedOperationException,
SQLException {
if (activeConnection == null) {
throw new SQLException("No active transaction");
}
activeConnection.rollback();
connectionPool.releaseConnection(activeConnection);
activeConnection = null;
}

public List<String> getPrimaryKeyColumns() {
return primaryKeyColumns;
}

public String getQueryString() {
return queryString;
}

public FreeformQueryDelegate getDelegate() {
return delegate;
}

public void setDelegate(FreeformQueryDelegate delegate) {
this.delegate = delegate;
}

/**
* This implementation of the containsRowWithKey method rewrites existing
* WHERE clauses in the query string. The logic is, however, not very
* complex and some times can do the Wrong Thing<sup>TM</sup>. For the
* situations where this logic is not enough, you can implement the
* getContainsRowQueryString method in FreeformQueryDelegate and this will
* be used instead of the logic.
*
* @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getContainsRowQueryString(Object...)
*
* {@inheritDoc}
*/
@SuppressWarnings("deprecation")
public boolean containsRowWithKey(Object... keys) throws SQLException {
String query = null;
boolean contains = false;
if (delegate != null) {
if (delegate instanceof FreeformStatementDelegate) {
try {
StatementHelper sh = ((FreeformStatementDelegate) delegate)
.getContainsRowQueryStatement(keys);
Connection c = getConnection();
PreparedStatement pstmt = c.prepareStatement(sh
.getQueryString());
sh.setParameterValuesToStatement(pstmt);
ResultSet rs = pstmt.executeQuery();
contains = rs.next();
rs.close();
pstmt.clearParameters();
pstmt.close();
releaseConnection(c);
return contains;
} catch (UnsupportedOperationException e) {
// Statement generation not supported, continue...
}
}
try {
query = delegate.getContainsRowQueryString(keys);
} catch (UnsupportedOperationException e) {
query = modifyWhereClause(keys);
}
} else {
query = modifyWhereClause(keys);
}
Connection conn = getConnection();
try {
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery(query);
contains = rs.next();
rs.close();
statement.close();
} finally {
releaseConnection(conn);
}
return contains;
}

/**
* Releases the connection if it is not part of an active transaction.
*
* @param conn
* the connection to release
*/
private void releaseConnection(Connection conn) {
if (conn != activeConnection) {
connectionPool.releaseConnection(conn);
}
}

private String modifyWhereClause(Object... keys) {
// Build the where rules for the provided keys
StringBuffer where = new StringBuffer();
for (int ix = 0; ix < primaryKeyColumns.size(); ix++) {
where.append(QueryBuilder.quote(primaryKeyColumns.get(ix)));
if (keys[ix] == null) {
where.append(" IS NULL");
} else {
where.append(" = '").append(keys[ix]).append("'");
}
if (ix < primaryKeyColumns.size() - 1) {
where.append(" AND ");
}
}
// Is there already a WHERE clause in the query string?
int index = queryString.toLowerCase().indexOf("where ");
if (index > -1) {
// Rewrite the where clause
return queryString.substring(0, index) + "WHERE " + where + " AND "
+ queryString.substring(index + 6);
}
// Append a where clause
return queryString + " WHERE " + where;
}

private void writeObject(java.io.ObjectOutputStream out) throws IOException {
try {
rollback();
} catch (SQLException ignored) {
}
out.defaultWriteObject();
}
}

+ 115
- 0
src/com/vaadin/data/util/query/FreeformQueryDelegate.java Visa fil

@@ -0,0 +1,115 @@
package com.vaadin.data.util.query;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.RowItem;

public interface FreeformQueryDelegate extends Serializable {
/**
* Should return the SQL query string to be performed. This method is
* responsible for gluing together the select query from the filters and the
* order by conditions if these are supported.
*
* @param offset
* the first record (row) to fetch.
* @param pagelength
* the number of records (rows) to fetch. 0 means all records
* starting from offset.
* @deprecated Implement {@link FreeformStatementDelegate} instead of
* {@link FreeformQueryDelegate}
*/
@Deprecated
public String getQueryString(int offset, int limit)
throws UnsupportedOperationException;

/**
* Generates and executes a query to determine the current row count from
* the DB. Row count will be fetched using filters that are currently set to
* the QueryDelegate.
*
* @return row count
* @throws SQLException
* @deprecated Implement {@link FreeformStatementDelegate} instead of
* {@link FreeformQueryDelegate}
*/
@Deprecated
public String getCountQuery() throws UnsupportedOperationException;

/**
* Sets the filters to apply when performing the SQL query. These are
* translated into a WHERE clause. Default filtering mode will be used.
*
* @param filters
* The filters to apply.
* @throws UnsupportedOperationException
* if the implementation doesn't support filtering.
*/
public void setFilters(List<Filter> filters)
throws UnsupportedOperationException;

/**
* Sets the order in which to retrieve rows from the database. The result
* can be ordered by zero or more columns and each column can be in
* ascending or descending order. These are translated into an ORDER BY
* clause in the SQL query.
*
* @param orderBys
* A list of the OrderBy conditions.
* @throws UnsupportedOperationException
* if the implementation doesn't support ordering.
*/
public void setOrderBy(List<OrderBy> orderBys)
throws UnsupportedOperationException;

/**
* Stores a row in the database. The implementation of this interface
* decides how to identify whether to store a new row or update an existing
* one.
*
* @param conn
* the JDBC connection to use
* @param row
* RowItem to be stored or updated.
* @throws UnsupportedOperationException
* if the implementation is read only.
* @throws SQLException
*/
public int storeRow(Connection conn, RowItem row)
throws UnsupportedOperationException, SQLException;

/**
* Removes the given RowItem from the database.
*
* @param conn
* the JDBC connection to use
* @param row
* RowItem to be removed
* @return true on success
* @throws UnsupportedOperationException
* @throws SQLException
*/
public boolean removeRow(Connection conn, RowItem row)
throws UnsupportedOperationException, SQLException;

/**
* Generates an SQL Query string that allows the user of the FreeformQuery
* class to customize the query string used by the
* FreeformQuery.containsRowWithKeys() method. This is useful for cases when
* the logic in the containsRowWithKeys method is not enough to support more
* complex free form queries.
*
* @param keys
* the values of the primary keys
* @throws UnsupportedOperationException
* to use the default logic in FreeformQuery
* @deprecated Implement {@link FreeformStatementDelegate} instead of
* {@link FreeformQueryDelegate}
*/
@Deprecated
public String getContainsRowQueryString(Object... keys)
throws UnsupportedOperationException;
}

+ 54
- 0
src/com/vaadin/data/util/query/FreeformStatementDelegate.java Visa fil

@@ -0,0 +1,54 @@
package com.vaadin.data.util.query;
import com.vaadin.data.util.query.generator.StatementHelper;
/**
* FreeformStatementDelegate is an extension to FreeformQueryDelegate that
* provides definitions for methods that produce StatementHelper objects instead
* of basic query strings. This allows the FreeformQuery query delegate to use
* PreparedStatements instead of regular Statement when accessing the database.
*
* Due to the injection protection and other benefits of prepared statements, it
* is advisable to implement this interface instead of the FreeformQueryDelegate
* whenever possible.
*/
public interface FreeformStatementDelegate extends FreeformQueryDelegate {
/**
* Should return a new instance of StatementHelper that contains the query
* string and parameter values required to create a PreparedStatement. This
* method is responsible for gluing together the select query from the
* filters and the order by conditions if these are supported.
*
* @param offset
* the first record (row) to fetch.
* @param pagelength
* the number of records (rows) to fetch. 0 means all records
* starting from offset.
*/
public StatementHelper getQueryStatement(int offset, int limit)
throws UnsupportedOperationException;
/**
* Should return a new instance of StatementHelper that contains the query
* string and parameter values required to create a PreparedStatement that
* will fetch the row count from the DB. Row count should be fetched using
* filters that are currently set to the QueryDelegate.
*/
public StatementHelper getCountStatement()
throws UnsupportedOperationException;
/**
* Should return a new instance of StatementHelper that contains the query
* string and parameter values required to create a PreparedStatement used
* by the FreeformQuery.containsRowWithKeys() method. This is useful for
* cases when the default logic in said method is not enough to support more
* complex free form queries.
*
* @param keys
* the values of the primary keys
* @throws UnsupportedOperationException
* to use the default logic in FreeformQuery
*/
public StatementHelper getContainsRowQueryStatement(Object... keys)
throws UnsupportedOperationException;
}

+ 43
- 0
src/com/vaadin/data/util/query/OrderBy.java Visa fil

@@ -0,0 +1,43 @@
package com.vaadin.data.util.query;

import java.io.Serializable;

/**
* OrderBy represents a sorting rule to be applied to a query made by the
* SQLContainer's QueryDelegate.
*
* The sorting rule is simple and contains only the affected column's name and
* the direction of the sort.
*/
public class OrderBy implements Serializable {
private String column;
private boolean isAscending;

/**
* Prevent instantiation without required parameters.
*/
@SuppressWarnings("unused")
private OrderBy() {
}

public OrderBy(String column, boolean isAscending) {
setColumn(column);
setAscending(isAscending);
}

public void setColumn(String column) {
this.column = column;
}

public String getColumn() {
return column;
}

public void setAscending(boolean isAscending) {
this.isAscending = isAscending;
}

public boolean isAscending() {
return isAscending;
}
}

+ 208
- 0
src/com/vaadin/data/util/query/QueryDelegate.java Visa fil

@@ -0,0 +1,208 @@
package com.vaadin.data.util.query;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.RowId;
import com.vaadin.data.util.RowItem;

public interface QueryDelegate extends Serializable {
/**
* Generates and executes a query to determine the current row count from
* the DB. Row count will be fetched using filters that are currently set to
* the QueryDelegate.
*
* @return row count
* @throws SQLException
*/
public int getCount() throws SQLException;

/**
* Executes a paged SQL query and returns the ResultSet. The query is
* defined through implementations of this QueryDelegate interface.
*
* @param offset
* the first item of the page to load
* @param pagelength
* the length of the page to load
* @return a ResultSet containing the rows of the page
* @throws SQLException
* if the database access fails.
*/
public ResultSet getResults(int offset, int pagelength) throws SQLException;

/**
* Allows the SQLContainer implementation to check whether the QueryDelegate
* implementation implements paging in the getResults method.
*
* @see QueryDelegate#getResults(int, int)
*
* @return true if the delegate implements paging
*/
public boolean implementationRespectsPagingLimits();

/**
* Sets the filters to apply when performing the SQL query. These are
* translated into a WHERE clause. Default filtering mode will be used.
*
* @param filters
* The filters to apply.
* @throws UnsupportedOperationException
* if the implementation doesn't support filtering.
*/
public void setFilters(List<Filter> filters)
throws UnsupportedOperationException;

/**
* Sets the order in which to retrieve rows from the database. The result
* can be ordered by zero or more columns and each column can be in
* ascending or descending order. These are translated into an ORDER BY
* clause in the SQL query.
*
* @param orderBys
* A list of the OrderBy conditions.
* @throws UnsupportedOperationException
* if the implementation doesn't support ordering.
*/
public void setOrderBy(List<OrderBy> orderBys)
throws UnsupportedOperationException;

/**
* Stores a row in the database. The implementation of this interface
* decides how to identify whether to store a new row or update an existing
* one.
*
* @param columnToValueMap
* A map containing the values for all columns to be stored or
* updated.
* @return the number of affected rows in the database table
* @throws UnsupportedOperationException
* if the implementation is read only.
*/
public int storeRow(RowItem row) throws UnsupportedOperationException,
SQLException;

/**
* Removes the given RowItem from the database.
*
* @param row
* RowItem to be removed
* @return true on success
* @throws UnsupportedOperationException
* @throws SQLException
*/
public boolean removeRow(RowItem row) throws UnsupportedOperationException,
SQLException;

/**
* Starts a new database transaction. Used when storing multiple changes.
*
* Note that if a transaction is already open, it will be rolled back when a
* new transaction is started.
*
* @throws SQLException
* if the database access fails.
*/
public void beginTransaction() throws SQLException;

/**
* Commits a transaction. If a transaction is not open nothing should
* happen.
*
* @throws SQLException
* if the database access fails.
*/
public void commit() throws SQLException;

/**
* Rolls a transaction back. If a transaction is not open nothing should
* happen.
*
* @throws SQLException
* if the database access fails.
*/
public void rollback() throws SQLException;

/**
* Returns a list of primary key column names. The list is either fetched
* from the database (TableQuery) or given as an argument depending on
* implementation.
*
* @return
*/
public List<String> getPrimaryKeyColumns();

/**
* Performs a query to find out whether the SQL table contains a row with
* the given set of primary keys.
*
* @param keys
* the primary keys
* @return true if the SQL table contains a row with the provided keys
* @throws SQLException
*/
public boolean containsRowWithKey(Object... keys) throws SQLException;

/************************/
/** ROWID CHANGE EVENT **/
/************************/

/**
* An <code>Event</code> object specifying the old and new RowId of an added
* item after the addition has been successfully committed.
*/
public interface RowIdChangeEvent extends Serializable {
/**
* Gets the old (temporary) RowId of the added row that raised this
* event.
*
* @return old RowId
*/
public RowId getOldRowId();

/**
* Gets the new, possibly database assigned RowId of the added row that
* raised this event.
*
* @return new RowId
*/
public RowId getNewRowId();
}

/** RowId change listener interface. */
public interface RowIdChangeListener extends Serializable {
/**
* Lets the listener know that a RowId has been changed.
*
* @param event
*/
public void rowIdChange(QueryDelegate.RowIdChangeEvent event);
}

/**
* The interface for adding and removing <code>RowIdChangeEvent</code>
* listeners. By implementing this interface a class explicitly announces
* that it will generate a <code>RowIdChangeEvent</code> when it performs a
* database commit that may change the RowId.
*/
public interface RowIdChangeNotifier extends Serializable {
/**
* Adds a RowIdChangeListener for the object.
*
* @param listener
* listener to be added
*/
public void addListener(QueryDelegate.RowIdChangeListener listener);

/**
* Removes the specified RowIdChangeListener from the object.
*
* @param listener
* listener to be removed
*/
public void removeListener(QueryDelegate.RowIdChangeListener listener);
}
}

+ 707
- 0
src/com/vaadin/data/util/query/TableQuery.java Visa fil

@@ -0,0 +1,707 @@
package com.vaadin.data.util.query;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EventObject;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.ColumnProperty;
import com.vaadin.data.util.OptimisticLockException;
import com.vaadin.data.util.RowId;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.SQLUtil;
import com.vaadin.data.util.TemporaryRowId;
import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.filter.Compare.Equal;
import com.vaadin.data.util.query.generator.DefaultSQLGenerator;
import com.vaadin.data.util.query.generator.MSSQLGenerator;
import com.vaadin.data.util.query.generator.SQLGenerator;
import com.vaadin.data.util.query.generator.StatementHelper;

@SuppressWarnings("serial")
public class TableQuery implements QueryDelegate,
QueryDelegate.RowIdChangeNotifier {

/** Table name, primary key column name(s) and version column name */
private String tableName;
private List<String> primaryKeyColumns;
private String versionColumn;

/** Currently set Filters and OrderBys */
private List<Filter> filters;
private List<OrderBy> orderBys;

/** SQLGenerator instance to use for generating queries */
private SQLGenerator sqlGenerator;

/** Fields related to Connection and Transaction handling */
private JDBCConnectionPool connectionPool;
private transient Connection activeConnection;
private boolean transactionOpen;

/** Row ID change listeners */
private LinkedList<RowIdChangeListener> rowIdChangeListeners;
/** Row ID change events, stored until commit() is called */
private final List<RowIdChangeEvent> bufferedEvents = new ArrayList<RowIdChangeEvent>();

/** Set to true to output generated SQL Queries to System.out */
private boolean debug = false;

/** Prevent no-parameters instantiation of TableQuery */
@SuppressWarnings("unused")
private TableQuery() {
}

/**
* Creates a new TableQuery using the given connection pool, SQL generator
* and table name to fetch the data from. All parameters must be non-null.
*
* @param tableName
* Name of the database table to connect to
* @param connectionPool
* Connection pool for accessing the database
* @param sqlGenerator
* SQL query generator implementation
*/
public TableQuery(String tableName, JDBCConnectionPool connectionPool,
SQLGenerator sqlGenerator) {
if (tableName == null || tableName.trim().length() < 1
|| connectionPool == null || sqlGenerator == null) {
throw new IllegalArgumentException(
"All parameters must be non-null and a table name must be given.");
}
this.tableName = tableName;
this.sqlGenerator = sqlGenerator;
this.connectionPool = connectionPool;
fetchMetaData();
}

/**
* Creates a new TableQuery using the given connection pool and table name
* to fetch the data from. All parameters must be non-null. The default SQL
* generator will be used for queries.
*
* @param tableName
* Name of the database table to connect to
* @param connectionPool
* Connection pool for accessing the database
*/
public TableQuery(String tableName, JDBCConnectionPool connectionPool) {
this(tableName, connectionPool, new DefaultSQLGenerator());
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getCount()
*/
public int getCount() throws SQLException {
debug("Fetching count...");
StatementHelper sh = sqlGenerator.generateSelectQuery(tableName,
filters, null, 0, 0, "COUNT(*)");
boolean shouldCloseTransaction = false;
if (!transactionOpen) {
shouldCloseTransaction = true;
beginTransaction();
}
ResultSet r = executeQuery(sh);
r.next();
int count = r.getInt(1);
r.getStatement().close();
r.close();
if (shouldCloseTransaction) {
commit();
}
return count;
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getResults(int,
* int)
*/
public ResultSet getResults(int offset, int pagelength) throws SQLException {
StatementHelper sh;
/*
* If no ordering is explicitly set, results will be ordered by the
* first primary key column.
*/
if (orderBys == null || orderBys.isEmpty()) {
List<OrderBy> ob = new ArrayList<OrderBy>();
ob.add(new OrderBy(primaryKeyColumns.get(0), true));
sh = sqlGenerator.generateSelectQuery(tableName, filters, ob,
offset, pagelength, null);
} else {
sh = sqlGenerator.generateSelectQuery(tableName, filters, orderBys,
offset, pagelength, null);
}
return executeQuery(sh);
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#
* implementationRespectsPagingLimits()
*/
public boolean implementationRespectsPagingLimits() {
return true;
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin
* .addon.sqlcontainer.RowItem)
*/
public int storeRow(RowItem row) throws UnsupportedOperationException,
SQLException {
if (row == null) {
throw new IllegalArgumentException("Row argument must be non-null.");
}
StatementHelper sh;
int result = 0;
if (row.getId() instanceof TemporaryRowId) {
setVersionColumnFlagInProperty(row);
sh = sqlGenerator.generateInsertQuery(tableName, row);
result = executeUpdateReturnKeys(sh, row);
} else {
setVersionColumnFlagInProperty(row);
sh = sqlGenerator.generateUpdateQuery(tableName, row);
result = executeUpdate(sh);
}
if (versionColumn != null && result == 0) {
throw new OptimisticLockException(
"Someone else changed the row that was being updated.",
row.getId());
}
return result;
}

private void setVersionColumnFlagInProperty(RowItem row) {
ColumnProperty versionProperty = (ColumnProperty) row
.getItemProperty(versionColumn);
if (versionProperty != null) {
versionProperty.setVersionColumn(true);
}
}

/**
* Inserts the given row in the database table immediately. Begins and
* commits the transaction needed. This method was added specifically to
* solve the problem of returning the final RowId immediately on the
* SQLContainer.addItem() call when auto commit mode is enabled in the
* SQLContainer.
*
* @param row
* RowItem to add to the database
* @return Final RowId of the added row
* @throws SQLException
*/
public RowId storeRowImmediately(RowItem row) throws SQLException {
beginTransaction();
/* Set version column, if one is provided */
setVersionColumnFlagInProperty(row);
/* Generate query */
StatementHelper sh = sqlGenerator.generateInsertQuery(tableName, row);
PreparedStatement pstmt = activeConnection.prepareStatement(
sh.getQueryString(), primaryKeyColumns.toArray(new String[0]));
sh.setParameterValuesToStatement(pstmt);
debug("DB -> " + sh.getQueryString());
int result = pstmt.executeUpdate();
if (result > 0) {
/*
* If affected rows exist, we'll get the new RowId, commit the
* transaction and return the new RowId.
*/
ResultSet generatedKeys = pstmt.getGeneratedKeys();
RowId newId = getNewRowId(row, generatedKeys);
generatedKeys.close();
pstmt.clearParameters();
pstmt.close();
commit();
return newId;
} else {
pstmt.clearParameters();
pstmt.close();
/* On failure return null */
return null;
}
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util
* .List)
*/
public void setFilters(List<Filter> filters)
throws UnsupportedOperationException {
if (filters == null) {
this.filters = null;
return;
}
this.filters = Collections.unmodifiableList(filters);
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.sqlcontainer.query.QueryDelegate#setOrderBy(java.util
* .List)
*/
public void setOrderBy(List<OrderBy> orderBys)
throws UnsupportedOperationException {
if (orderBys == null) {
this.orderBys = null;
return;
}
this.orderBys = Collections.unmodifiableList(orderBys);
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#beginTransaction()
*/
public void beginTransaction() throws UnsupportedOperationException,
SQLException {
if (transactionOpen && activeConnection != null) {
throw new IllegalStateException();
}
debug("DB -> begin transaction");
activeConnection = connectionPool.reserveConnection();
activeConnection.setAutoCommit(false);
transactionOpen = true;
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#commit()
*/
public void commit() throws UnsupportedOperationException, SQLException {
if (transactionOpen && activeConnection != null) {
debug("DB -> commit");
activeConnection.commit();
connectionPool.releaseConnection(activeConnection);
} else {
throw new SQLException("No active transaction");
}
transactionOpen = false;

/* Handle firing row ID change events */
RowIdChangeEvent[] unFiredEvents = bufferedEvents
.toArray(new RowIdChangeEvent[] {});
bufferedEvents.clear();
if (rowIdChangeListeners != null && !rowIdChangeListeners.isEmpty()) {
for (RowIdChangeListener r : rowIdChangeListeners) {
for (RowIdChangeEvent e : unFiredEvents) {
r.rowIdChange(e);
}
}
}
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#rollback()
*/
public void rollback() throws UnsupportedOperationException, SQLException {
if (transactionOpen && activeConnection != null) {
debug("DB -> rollback");
activeConnection.rollback();
connectionPool.releaseConnection(activeConnection);
} else {
throw new SQLException("No active transaction");
}
transactionOpen = false;
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.sqlcontainer.query.QueryDelegate#getPrimaryKeyColumns()
*/
public List<String> getPrimaryKeyColumns() {
return Collections.unmodifiableList(primaryKeyColumns);
}

public String getVersionColumn() {
return versionColumn;
}

public void setVersionColumn(String column) {
versionColumn = column;
}

public String getTableName() {
return tableName;
}

public SQLGenerator getSqlGenerator() {
return sqlGenerator;
}

/**
* Executes the given query string using either the active connection if a
* transaction is already open, or a new connection from this query's
* connection pool.
*
* @param sh
* an instance of StatementHelper, containing the query string
* and parameter values.
* @return ResultSet of the query
* @throws SQLException
*/
private ResultSet executeQuery(StatementHelper sh) throws SQLException {
Connection c = null;
if (transactionOpen && activeConnection != null) {
c = activeConnection;
} else {
throw new SQLException("No active transaction!");
}
PreparedStatement pstmt = c.prepareStatement(sh.getQueryString());
sh.setParameterValuesToStatement(pstmt);
debug("DB -> " + sh.getQueryString());
return pstmt.executeQuery();
}

/**
* Executes the given update query string using either the active connection
* if a transaction is already open, or a new connection from this query's
* connection pool.
*
* @param sh
* an instance of StatementHelper, containing the query string
* and parameter values.
* @return Number of affected rows
* @throws SQLException
*/
private int executeUpdate(StatementHelper sh) throws SQLException {
Connection c = null;
PreparedStatement pstmt = null;
try {
if (transactionOpen && activeConnection != null) {
c = activeConnection;
} else {
c = connectionPool.reserveConnection();
}
pstmt = c.prepareStatement(sh.getQueryString());
sh.setParameterValuesToStatement(pstmt);
debug("DB -> " + sh.getQueryString());
int retval = pstmt.executeUpdate();
return retval;
} finally {
if (pstmt != null) {
pstmt.clearParameters();
pstmt.close();
}
if (!transactionOpen) {
connectionPool.releaseConnection(c);
}
}
}

/**
* Executes the given update query string using either the active connection
* if a transaction is already open, or a new connection from this query's
* connection pool.
*
* Additionally adds a new RowIdChangeEvent to the event buffer.
*
* @param sh
* an instance of StatementHelper, containing the query string
* and parameter values.
* @param row
* the row item to update
* @return Number of affected rows
* @throws SQLException
*/
private int executeUpdateReturnKeys(StatementHelper sh, RowItem row)
throws SQLException {
Connection c = null;
PreparedStatement pstmt = null;
ResultSet genKeys = null;
try {
if (transactionOpen && activeConnection != null) {
c = activeConnection;
} else {
c = connectionPool.reserveConnection();
}
pstmt = c.prepareStatement(sh.getQueryString(),
primaryKeyColumns.toArray(new String[0]));
sh.setParameterValuesToStatement(pstmt);
debug("DB -> " + sh.getQueryString());
int result = pstmt.executeUpdate();
genKeys = pstmt.getGeneratedKeys();
RowId newId = getNewRowId(row, genKeys);
bufferedEvents.add(new RowIdChangeEvent(row.getId(), newId));
return result;
} finally {
if (genKeys != null) {
genKeys.close();
}
if (pstmt != null) {
pstmt.clearParameters();
pstmt.close();
}
if (!transactionOpen) {
connectionPool.releaseConnection(c);
}
}
}

/**
* Fetches name(s) of primary key column(s) from DB metadata.
*
* Also tries to get the escape string to be used in search strings.
*/
private void fetchMetaData() {
Connection c = null;
try {
c = connectionPool.reserveConnection();
DatabaseMetaData dbmd = c.getMetaData();
if (dbmd != null) {
tableName = SQLUtil.escapeSQL(tableName);
ResultSet tables = dbmd.getTables(null, null, tableName, null);
if (!tables.next()) {
tables = dbmd.getTables(null, null,
tableName.toUpperCase(), null);
if (!tables.next()) {
throw new IllegalArgumentException(
"Table with the name \""
+ tableName
+ "\" was not found. Check your database contents.");
} else {
tableName = tableName.toUpperCase();
}
}
tables.close();
ResultSet rs = dbmd.getPrimaryKeys(null, null, tableName);
List<String> names = new ArrayList<String>();
while (rs.next()) {
names.add(rs.getString("COLUMN_NAME"));
}
rs.close();
if (!names.isEmpty()) {
primaryKeyColumns = names;
}
if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
throw new IllegalArgumentException(
"Primary key constraints have not been defined for the table \""
+ tableName
+ "\". Use FreeFormQuery to access this table.");
}
for (String colName : primaryKeyColumns) {
if (colName.equalsIgnoreCase("rownum")) {
if (getSqlGenerator() instanceof MSSQLGenerator
|| getSqlGenerator() instanceof MSSQLGenerator) {
throw new IllegalArgumentException(
"When using Oracle or MSSQL, a primary key column"
+ " named \'rownum\' is not allowed!");
}
}
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
connectionPool.releaseConnection(c);
}
}

private RowId getNewRowId(RowItem row, ResultSet genKeys) {
try {
/* Fetch primary key values and generate a map out of them. */
Map<String, Object> values = new HashMap<String, Object>();
ResultSetMetaData rsmd = genKeys.getMetaData();
int colCount = rsmd.getColumnCount();
if (genKeys.next()) {
for (int i = 1; i <= colCount; i++) {
values.put(rsmd.getColumnName(i), genKeys.getObject(i));
}
}
/* Generate new RowId */
List<Object> newRowId = new ArrayList<Object>();
if (values.size() == 1) {
if (primaryKeyColumns.size() == 1) {
newRowId.add(values.get(values.keySet().iterator().next()));
} else {
for (String s : primaryKeyColumns) {
if (!((ColumnProperty) row.getItemProperty(s))
.isReadOnlyChangeAllowed()) {
newRowId.add(values.get(values.keySet().iterator()
.next()));
} else {
newRowId.add(values.get(s));
}
}
}
} else {
for (String s : primaryKeyColumns) {
newRowId.add(values.get(s));
}
}
return new RowId(newRowId.toArray());
} catch (Exception e) {
debug("Failed to fetch key values on insert: " + e.getMessage());
return null;
}
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.sqlcontainer.query.QueryDelegate#removeRow(com.vaadin
* .addon.sqlcontainer.RowItem)
*/
public boolean removeRow(RowItem row) throws UnsupportedOperationException,
SQLException {
debug("Removing row with id: " + row.getId().getId()[0].toString());
if (executeUpdate(sqlGenerator.generateDeleteQuery(getTableName(),
primaryKeyColumns, versionColumn, row)) == 1) {
return true;
}
if (versionColumn != null) {
throw new OptimisticLockException(
"Someone else changed the row that was being deleted.",
row.getId());
}
return false;
}

/*
* (non-Javadoc)
*
* @see
* com.vaadin.addon.sqlcontainer.query.QueryDelegate#containsRowWithKey(
* java.lang.Object[])
*/
public boolean containsRowWithKey(Object... keys) throws SQLException {
ArrayList<Filter> filtersAndKeys = new ArrayList<Filter>();
if (filters != null) {
filtersAndKeys.addAll(filters);
}
int ix = 0;
for (String colName : primaryKeyColumns) {
filtersAndKeys.add(new Equal(colName, keys[ix]));
ix++;
}
StatementHelper sh = sqlGenerator.generateSelectQuery(tableName,
filtersAndKeys, orderBys, 0, 0, "*");

boolean shouldCloseTransaction = false;
if (!transactionOpen) {
shouldCloseTransaction = true;
beginTransaction();
}
ResultSet rs = null;
try {
rs = executeQuery(sh);
boolean contains = rs.next();
return contains;
} finally {
if (rs != null) {
if (rs.getStatement() != null) {
rs.getStatement().close();
}
rs.close();
}
if (shouldCloseTransaction) {
commit();
}
}
}

/**
* Output a debug message
*
* @param message
*/
private void debug(String message) {
if (debug) {
System.out.println(message);
}
}

/**
* Enable or disable debug mode.
*
* @param debug
*/
public void setDebug(boolean debug) {
this.debug = debug;
}

/**
* Custom writeObject to call rollback() if object is serialized.
*/
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
try {
rollback();
} catch (SQLException ignored) {
}
out.defaultWriteObject();
}

/**
* Simple RowIdChangeEvent implementation.
*/
public class RowIdChangeEvent extends EventObject implements
QueryDelegate.RowIdChangeEvent {
private final RowId oldId;
private final RowId newId;

private RowIdChangeEvent(RowId oldId, RowId newId) {
super(oldId);
this.oldId = oldId;
this.newId = newId;
}

public RowId getNewRowId() {
return newId;
}

public RowId getOldRowId() {
return oldId;
}
}

/**
* Adds RowIdChangeListener to this query
*/
public void addListener(RowIdChangeListener listener) {
if (rowIdChangeListeners == null) {
rowIdChangeListeners = new LinkedList<QueryDelegate.RowIdChangeListener>();
}
rowIdChangeListeners.add(listener);
}

/**
* Removes the given RowIdChangeListener from this query
*/
public void removeListener(RowIdChangeListener listener) {
if (rowIdChangeListeners != null) {
rowIdChangeListeners.remove(listener);
}
}
}

+ 308
- 0
src/com/vaadin/data/util/query/generator/DefaultSQLGenerator.java Visa fil

@@ -0,0 +1,308 @@
package com.vaadin.data.util.query.generator;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.ColumnProperty;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.SQLUtil;
import com.vaadin.data.util.TemporaryRowId;
import com.vaadin.data.util.query.OrderBy;
import com.vaadin.data.util.query.generator.filter.QueryBuilder;
import com.vaadin.data.util.query.generator.filter.StringDecorator;

/**
* Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL.
*
* @author Jonatan Kronqvist / IT Mill Ltd
*/
@SuppressWarnings("serial")
public class DefaultSQLGenerator implements SQLGenerator {

public DefaultSQLGenerator() {

}

/**
* Construct a DefaultSQLGenerator with the specified identifiers for start
* and end of quoted strings. The identifiers may be different depending on
* the database engine and it's settings.
*
* @param quoteStart
* the identifier (character) denoting the start of a quoted
* string
* @param quoteEnd
* the identifier (character) denoting the end of a quoted string
*/
public DefaultSQLGenerator(String quoteStart, String quoteEnd) {
QueryBuilder.setStringDecorator(new StringDecorator(quoteStart,
quoteEnd));
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateSelectQuery(java.lang.String, java.util.List, java.util.List,
* int, int, java.lang.String)
*/
public StatementHelper generateSelectQuery(String tableName,
List<Filter> filters, List<OrderBy> orderBys, int offset,
int pagelength, String toSelect) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
toSelect = toSelect == null ? "*" : toSelect;
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("SELECT " + toSelect + " FROM ").append(
SQLUtil.escapeSQL(tableName));
if (filters != null) {
query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
}
if (orderBys != null) {
for (OrderBy o : orderBys) {
generateOrderBy(query, o, orderBys.indexOf(o) == 0);
}
}
if (pagelength != 0) {
generateLimits(query, offset, pagelength);
}
sh.setQueryString(query.toString());
return sh;
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateUpdateQuery(java.lang.String,
* com.vaadin.addon.sqlcontainer.RowItem)
*/
public StatementHelper generateUpdateQuery(String tableName, RowItem item) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
if (item == null) {
throw new IllegalArgumentException("Updated item must be given.");
}
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("UPDATE ").append(tableName).append(" SET");

/* Generate column<->value and rowidentifiers map */
Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
Map<String, Object> rowIdentifiers = generateRowIdentifiers(item);
/* Generate columns and values to update */
boolean first = true;
for (String column : columnToValueMap.keySet()) {
if (first) {
query.append(" " + QueryBuilder.quote(column) + " = ?");
} else {
query.append(", " + QueryBuilder.quote(column) + " = ?");
}
sh.addParameterValue(columnToValueMap.get(column), item
.getItemProperty(column).getType());
first = false;
}
/* Generate identifiers for the row to be updated */
first = true;
for (String column : rowIdentifiers.keySet()) {
if (first) {
query.append(" WHERE " + QueryBuilder.quote(column) + " = ?");
} else {
query.append(" AND " + QueryBuilder.quote(column) + " = ?");
}
sh.addParameterValue(rowIdentifiers.get(column), item
.getItemProperty(column).getType());
first = false;
}
sh.setQueryString(query.toString());
return sh;
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateInsertQuery(java.lang.String,
* com.vaadin.addon.sqlcontainer.RowItem)
*/
public StatementHelper generateInsertQuery(String tableName, RowItem item) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
if (item == null) {
throw new IllegalArgumentException("New item must be given.");
}
if (!(item.getId() instanceof TemporaryRowId)) {
throw new IllegalArgumentException(
"Cannot generate an insert query for item already in database.");
}
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("INSERT INTO ").append(tableName).append(" (");

/* Generate column<->value map */
Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
/* Generate column names for insert query */
boolean first = true;
for (String column : columnToValueMap.keySet()) {
if (!first) {
query.append(", ");
}
query.append(QueryBuilder.quote(column));
first = false;
}

/* Generate values for insert query */
query.append(") VALUES (");
first = true;
for (String column : columnToValueMap.keySet()) {
if (!first) {
query.append(", ");
}
query.append("?");
sh.addParameterValue(columnToValueMap.get(column), item
.getItemProperty(column).getType());
first = false;
}
query.append(")");
sh.setQueryString(query.toString());
return sh;
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
* generateDeleteQuery(java.lang.String,
* com.vaadin.addon.sqlcontainer.RowItem)
*/
public StatementHelper generateDeleteQuery(String tableName,
List<String> primaryKeyColumns, String versionColumn, RowItem item) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
if (item == null) {
throw new IllegalArgumentException(
"Item to be deleted must be given.");
}
if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
throw new IllegalArgumentException(
"Valid keyColumnNames must be provided.");
}
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();
query.append("DELETE FROM ").append(tableName).append(" WHERE ");
int count = 1;
for (String keyColName : primaryKeyColumns) {
if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
&& keyColName.equalsIgnoreCase("rownum")) {
count++;
continue;
}
if (count > 1) {
query.append(" AND ");
}
if (item.getItemProperty(keyColName).getValue() != null) {
query.append(QueryBuilder.quote(keyColName) + " = ?");
sh.addParameterValue(item.getItemProperty(keyColName)
.getValue(), item.getItemProperty(keyColName).getType());
}
count++;
}
if (versionColumn != null) {
query.append(String.format(" AND %s = ?",
QueryBuilder.quote(versionColumn)));
sh.addParameterValue(
item.getItemProperty(versionColumn).getValue(), item
.getItemProperty(versionColumn).getType());
}

sh.setQueryString(query.toString());
return sh;
}

/**
* Generates sorting rules as an ORDER BY -clause
*
* @param sb
* StringBuffer to which the clause is appended.
* @param o
* OrderBy object to be added into the sb.
* @param firstOrderBy
* If true, this is the first OrderBy.
* @return
*/
protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o,
boolean firstOrderBy) {
if (firstOrderBy) {
sb.append(" ORDER BY ");
} else {
sb.append(", ");
}
sb.append(QueryBuilder.quote(o.getColumn()));
if (o.isAscending()) {
sb.append(" ASC");
} else {
sb.append(" DESC");
}
return sb;
}

/**
* Generates the LIMIT and OFFSET clause.
*
* @param sb
* StringBuffer to which the clause is appended.
* @param offset
* Value for offset.
* @param pagelength
* Value for pagelength.
* @return StringBuffer with LIMIT and OFFSET clause added.
*/
protected StringBuffer generateLimits(StringBuffer sb, int offset,
int pagelength) {
sb.append(" LIMIT ").append(pagelength).append(" OFFSET ")
.append(offset);
return sb;
}

protected Map<String, Object> generateColumnToValueMap(RowItem item) {
Map<String, Object> columnToValueMap = new HashMap<String, Object>();
for (Object id : item.getItemPropertyIds()) {
ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
/* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
&& cp.getPropertyId().equalsIgnoreCase("rownum")) {
continue;
}
Object value = cp.getValue() == null ? null : cp.getValue();
/* Only include properties whose read-only status can be altered */
if (cp.isReadOnlyChangeAllowed() && !cp.isVersionColumn()) {
columnToValueMap.put(cp.getPropertyId(), value);
}
}
return columnToValueMap;
}

protected Map<String, Object> generateRowIdentifiers(RowItem item) {
Map<String, Object> rowIdentifiers = new HashMap<String, Object>();
for (Object id : item.getItemPropertyIds()) {
ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
/* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
&& cp.getPropertyId().equalsIgnoreCase("rownum")) {
continue;
}
Object value = cp.getValue() == null ? null : cp.getValue();
if (!cp.isReadOnlyChangeAllowed() || cp.isVersionColumn()) {
rowIdentifiers.put(cp.getPropertyId(), value);
}
}
return rowIdentifiers;
}
}

+ 101
- 0
src/com/vaadin/data/util/query/generator/MSSQLGenerator.java Visa fil

@@ -0,0 +1,101 @@
package com.vaadin.data.util.query.generator;

import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.query.OrderBy;
import com.vaadin.data.util.query.generator.filter.QueryBuilder;

@SuppressWarnings("serial")
public class MSSQLGenerator extends DefaultSQLGenerator {

public MSSQLGenerator() {

}

/**
* Construct a MSSQLGenerator with the specified identifiers for start and
* end of quoted strings. The identifiers may be different depending on the
* database engine and it's settings.
*
* @param quoteStart
* the identifier (character) denoting the start of a quoted
* string
* @param quoteEnd
* the identifier (character) denoting the end of a quoted string
*/
public MSSQLGenerator(String quoteStart, String quoteEnd) {
super(quoteStart, quoteEnd);
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator#
* generateSelectQuery(java.lang.String, java.util.List,
* com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int,
* int, java.lang.String)
*/
@Override
public StatementHelper generateSelectQuery(String tableName,
List<Filter> filters, List<OrderBy> orderBys, int offset,
int pagelength, String toSelect) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
/* Adjust offset and page length parameters to match "row numbers" */
offset = pagelength > 1 ? ++offset : offset;
pagelength = pagelength > 1 ? --pagelength : pagelength;
toSelect = toSelect == null ? "*" : toSelect;
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();

/* Row count request is handled here */
if ("COUNT(*)".equalsIgnoreCase(toSelect)) {
query.append(String.format(
"SELECT COUNT(*) AS %s FROM (SELECT * FROM %s",
QueryBuilder.quote("rowcount"), tableName));
if (filters != null && !filters.isEmpty()) {
query.append(QueryBuilder.getWhereStringForFilters(
filters, sh));
}
query.append(") AS t");
sh.setQueryString(query.toString());
return sh;
}

/* SELECT without row number constraints */
if (offset == 0 && pagelength == 0) {
query.append("SELECT ").append(toSelect).append(" FROM ")
.append(tableName);
if (filters != null) {
query.append(QueryBuilder.getWhereStringForFilters(
filters, sh));
}
if (orderBys != null) {
for (OrderBy o : orderBys) {
generateOrderBy(query, o, orderBys.indexOf(o) == 0);
}
}
sh.setQueryString(query.toString());
return sh;
}

/* Remaining SELECT cases are handled here */
query.append("SELECT * FROM (SELECT row_number() OVER (");
if (orderBys != null) {
for (OrderBy o : orderBys) {
generateOrderBy(query, o, orderBys.indexOf(o) == 0);
}
}
query.append(") AS rownum, " + toSelect + " FROM ").append(tableName);
if (filters != null) {
query.append(QueryBuilder.getWhereStringForFilters(
filters, sh));
}
query.append(") AS a WHERE a.rownum BETWEEN ").append(offset)
.append(" AND ").append(Integer.toString(offset + pagelength));
sh.setQueryString(query.toString());
return sh;
}
}

+ 99
- 0
src/com/vaadin/data/util/query/generator/OracleGenerator.java Visa fil

@@ -0,0 +1,99 @@
package com.vaadin.data.util.query.generator;

import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.query.OrderBy;
import com.vaadin.data.util.query.generator.filter.QueryBuilder;

@SuppressWarnings("serial")
public class OracleGenerator extends DefaultSQLGenerator {

public OracleGenerator() {

}

/**
* Construct an OracleSQLGenerator with the specified identifiers for start
* and end of quoted strings. The identifiers may be different depending on
* the database engine and it's settings.
*
* @param quoteStart
* the identifier (character) denoting the start of a quoted
* string
* @param quoteEnd
* the identifier (character) denoting the end of a quoted string
*/
public OracleGenerator(String quoteStart, String quoteEnd) {
super(quoteStart, quoteEnd);
}

/*
* (non-Javadoc)
*
* @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator#
* generateSelectQuery(java.lang.String, java.util.List,
* com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int,
* int, java.lang.String)
*/
@Override
public StatementHelper generateSelectQuery(String tableName,
List<Filter> filters, List<OrderBy> orderBys, int offset,
int pagelength, String toSelect) {
if (tableName == null || tableName.trim().equals("")) {
throw new IllegalArgumentException("Table name must be given.");
}
/* Adjust offset and page length parameters to match "row numbers" */
offset = pagelength > 1 ? ++offset : offset;
pagelength = pagelength > 1 ? --pagelength : pagelength;
toSelect = toSelect == null ? "*" : toSelect;
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer();

/* Row count request is handled here */
if ("COUNT(*)".equalsIgnoreCase(toSelect)) {
query.append(String.format(
"SELECT COUNT(*) AS %s FROM (SELECT * FROM %s",
QueryBuilder.quote("rowcount"), tableName));
if (filters != null && !filters.isEmpty()) {
query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
}
query.append(")");
sh.setQueryString(query.toString());
return sh;
}

/* SELECT without row number constraints */
if (offset == 0 && pagelength == 0) {
query.append("SELECT ").append(toSelect).append(" FROM ")
.append(tableName);
if (filters != null) {
query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
}
if (orderBys != null) {
for (OrderBy o : orderBys) {
generateOrderBy(query, o, orderBys.indexOf(o) == 0);
}
}
sh.setQueryString(query.toString());
return sh;
}

/* Remaining SELECT cases are handled here */
query.append(String
.format("SELECT * FROM (SELECT x.*, ROWNUM AS %s FROM (SELECT %s FROM %s",
QueryBuilder.quote("rownum"), toSelect, tableName));
if (filters != null) {
query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
}
if (orderBys != null) {
for (OrderBy o : orderBys) {
generateOrderBy(query, o, orderBys.indexOf(o) == 0);
}
}
query.append(String.format(") x) WHERE %s BETWEEN %d AND %d",
QueryBuilder.quote("rownum"), offset, offset + pagelength));
sh.setQueryString(query.toString());
return sh;
}
}

+ 85
- 0
src/com/vaadin/data/util/query/generator/SQLGenerator.java Visa fil

@@ -0,0 +1,85 @@
package com.vaadin.data.util.query.generator;

import java.io.Serializable;
import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.query.OrderBy;

/**
* The SQLGenerator interface is meant to be implemented for each different SQL
* syntax that is to be supported. By default there are implementations for
* HSQLDB, MySQL, PostgreSQL, MSSQL and Oracle syntaxes.
*
* @author Jonatan Kronqvist / IT Mill Ltd
*/
public interface SQLGenerator extends Serializable {
/**
* Generates a SELECT query with the provided parameters. Uses default
* filtering mode (INCLUSIVE).
*
* @param tableName
* Name of the table queried
* @param filters
* The filters, converted into a WHERE clause
* @param orderBys
* The the ordering conditions, converted into an ORDER BY clause
* @param offset
* The offset of the first row to be included
* @param pagelength
* The number of rows to be returned when the query executes
* @param toSelect
* String containing what to select, e.g. "*", "COUNT(*)"
* @return StatementHelper instance containing the query string for a
* PreparedStatement and the values required for the parameters
*/
public StatementHelper generateSelectQuery(String tableName,
List<Filter> filters, List<OrderBy> orderBys, int offset,
int pagelength, String toSelect);

/**
* Generates an UPDATE query with the provided parameters.
*
* @param tableName
* Name of the table queried
* @param item
* RowItem containing the updated values update.
* @return StatementHelper instance containing the query string for a
* PreparedStatement and the values required for the parameters
*/
public StatementHelper generateUpdateQuery(String tableName, RowItem item);

/**
* Generates an INSERT query for inserting a new row with the provided
* values.
*
* @param tableName
* Name of the table queried
* @param item
* New RowItem to be inserted into the database.
* @return StatementHelper instance containing the query string for a
* PreparedStatement and the values required for the parameters
*/
public StatementHelper generateInsertQuery(String tableName, RowItem item);

/**
* Generates a DELETE query for deleting data related to the given RowItem
* from the database.
*
* @param tableName
* Name of the table queried
* @param primaryKeyColumns
* the names of the columns holding the primary key. Usually just
* one column, but might be several.
* @param versionColumn
* the column containing the version number of the row, null if
* versioning (optimistic locking) not enabled.
* @param item
* Item to be deleted from the database
* @return StatementHelper instance containing the query string for a
* PreparedStatement and the values required for the parameters
*/
public StatementHelper generateDeleteQuery(String tableName,
List<String> primaryKeyColumns, String versionColumn, RowItem item);
}

+ 131
- 0
src/com/vaadin/data/util/query/generator/StatementHelper.java Visa fil

@@ -0,0 +1,131 @@
package com.vaadin.data.util.query.generator;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* StatementHelper is a simple helper class that assists TableQuery and the
* query generators in filling a PreparedStatement. The actual statement is
* generated by the query generator methods, but the resulting statement and all
* the parameter values are stored in an instance of StatementHelper.
*
* This class will also fill the values with correct setters into the
* PreparedStatement on request.
*/
public class StatementHelper {
private String queryString;
private List<Object> parameters = new ArrayList<Object>();
private Map<Integer, Class<?>> dataTypes = new HashMap<Integer, Class<?>>();
public StatementHelper() {
}
public void setQueryString(String queryString) {
this.queryString = queryString;
}
public String getQueryString() {
return queryString;
}
public void addParameterValue(Object parameter) {
if (parameter != null) {
parameters.add(parameter);
dataTypes.put(parameters.size() - 1, parameter.getClass());
}
}
public void addParameterValue(Object parameter, Class<?> type) {
parameters.add(parameter);
dataTypes.put(parameters.size() - 1, type);
}
public void setParameterValuesToStatement(PreparedStatement pstmt)
throws SQLException {
for (int i = 0; i < parameters.size(); i++) {
if (parameters.get(i) == null) {
handleNullValue(i, pstmt);
} else {
pstmt.setObject(i + 1, parameters.get(i));
}
}
/*
* The following list contains the data types supported by
* PreparedStatement but not supported by SQLContainer:
*
* [The list is provided as PreparedStatement method signatures]
*
* setNCharacterStream(int parameterIndex, Reader value)
*
* setNClob(int parameterIndex, NClob value)
*
* setNString(int parameterIndex, String value)
*
* setRef(int parameterIndex, Ref x)
*
* setRowId(int parameterIndex, RowId x)
*
* setSQLXML(int parameterIndex, SQLXML xmlObject)
*
* setBytes(int parameterIndex, byte[] x)
*
* setCharacterStream(int parameterIndex, Reader reader)
*
* setClob(int parameterIndex, Clob x)
*
* setURL(int parameterIndex, URL x)
*
* setArray(int parameterIndex, Array x)
*
* setAsciiStream(int parameterIndex, InputStream x)
*
* setBinaryStream(int parameterIndex, InputStream x)
*
* setBlob(int parameterIndex, Blob x)
*/
}
private void handleNullValue(int i, PreparedStatement pstmt)
throws SQLException {
if (BigDecimal.class.equals(dataTypes.get(i))) {
pstmt.setBigDecimal(i + 1, null);
} else if (Boolean.class.equals(dataTypes.get(i))) {
pstmt.setNull(i + 1, Types.BOOLEAN);
} else if (Byte.class.equals(dataTypes.get(i))) {
pstmt.setNull(i + 1, Types.SMALLINT);
} else if (Date.class.equals(dataTypes.get(i))) {
pstmt.setDate(i + 1, null);
} else if (Double.class.equals(dataTypes.get(i))) {
pstmt.setNull(i + 1, Types.DOUBLE);
} else if (Float.class.equals(dataTypes.get(i))) {
pstmt.setNull(i + 1, Types.FLOAT);
} else if (Integer.class.equals(dataTypes.get(i))) {
pstmt.setNull(i + 1, Types.INTEGER);
} else if (Long.class.equals(dataTypes.get(i))) {
pstmt.setNull(i + 1, Types.BIGINT);
} else if (Short.class.equals(dataTypes.get(i))) {
pstmt.setNull(i + 1, Types.SMALLINT);
} else if (String.class.equals(dataTypes.get(i))) {
pstmt.setString(i + 1, null);
} else if (Time.class.equals(dataTypes.get(i))) {
pstmt.setTime(i + 1, null);
} else if (Timestamp.class.equals(dataTypes.get(i))) {
pstmt.setTimestamp(i + 1, null);
} else {
throw new SQLException("Data type not supported by SQLContainer: "
+ parameters.get(i).getClass().toString());
}
}
}

+ 18
- 0
src/com/vaadin/data/util/query/generator/filter/AndTranslator.java Visa fil

@@ -0,0 +1,18 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.And;
import com.vaadin.data.util.query.generator.StatementHelper;

public class AndTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof And;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
return QueryBuilder.group(QueryBuilder
.getJoinedFilterString(((And) filter).getFilters(), "AND", sh));
}

}

+ 21
- 0
src/com/vaadin/data/util/query/generator/filter/BetweenTranslator.java Visa fil

@@ -0,0 +1,21 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.Between;
import com.vaadin.data.util.query.generator.StatementHelper;

public class BetweenTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof Between;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
Between between = (Between) filter;
sh.addParameterValue(between.getStartValue());
sh.addParameterValue(between.getEndValue());
return QueryBuilder.quote(between.getPropertyId())
+ " BETWEEN ? AND ?";
}

}

+ 33
- 0
src/com/vaadin/data/util/query/generator/filter/CompareTranslator.java Visa fil

@@ -0,0 +1,33 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.Compare;
import com.vaadin.data.util.query.generator.StatementHelper;

public class CompareTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof Compare;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
Compare compare = (Compare) filter;
sh.addParameterValue(compare.getValue());
String prop = QueryBuilder.quote(compare.getPropertyId());
switch (compare.getOperation()) {
case EQUAL:
return prop + " = ?";
case GREATER:
return prop + " > ?";
case GREATER_OR_EQUAL:
return prop + " >= ?";
case LESS:
return prop + " < ?";
case LESS_OR_EQUAL:
return prop + " <= ?";
default:
return "";
}
}

}

+ 11
- 0
src/com/vaadin/data/util/query/generator/filter/FilterTranslator.java Visa fil

@@ -0,0 +1,11 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.query.generator.StatementHelper;

public interface FilterTranslator {
public boolean translatesFilter(Filter filter);

public String getWhereStringForFilter(Filter filter, StatementHelper sh);

}

+ 17
- 0
src/com/vaadin/data/util/query/generator/filter/IsNullTranslator.java Visa fil

@@ -0,0 +1,17 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.IsNull;
import com.vaadin.data.util.query.generator.StatementHelper;

public class IsNullTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof IsNull;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
IsNull in = (IsNull) filter;
return QueryBuilder.quote(in.getPropertyId()) + " IS NULL";
}
}

+ 27
- 0
src/com/vaadin/data/util/query/generator/filter/LikeTranslator.java Visa fil

@@ -0,0 +1,27 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.Like;
import com.vaadin.data.util.query.generator.StatementHelper;

public class LikeTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof Like;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
Like like = (Like) filter;
if (like.isCaseSensitive()) {
sh.addParameterValue(like.getValue());
return QueryBuilder.quote(like.getPropertyId())
+ " LIKE ?";
} else {
sh.addParameterValue(like.getValue().toUpperCase());
return "UPPER("
+ QueryBuilder.quote(like.getPropertyId())
+ ") LIKE ?";
}
}

}

+ 26
- 0
src/com/vaadin/data/util/query/generator/filter/NotTranslator.java Visa fil

@@ -0,0 +1,26 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.IsNull;
import com.vaadin.data.util.filter.Not;
import com.vaadin.data.util.query.generator.StatementHelper;

public class NotTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof Not;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
Not not = (Not) filter;
if (not.getFilter() instanceof IsNull) {
IsNull in = (IsNull) not.getFilter();
return QueryBuilder.quote(in.getPropertyId())
+ " IS NOT NULL";
}
return "NOT "
+ QueryBuilder.getWhereStringForFilter(
not.getFilter(), sh);
}

}

+ 18
- 0
src/com/vaadin/data/util/query/generator/filter/OrTranslator.java Visa fil

@@ -0,0 +1,18 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.Or;
import com.vaadin.data.util.query.generator.StatementHelper;

public class OrTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof Or;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
return QueryBuilder.group(QueryBuilder
.getJoinedFilterString(((Or) filter).getFilters(), "OR", sh));
}

}

+ 94
- 0
src/com/vaadin/data/util/query/generator/filter/QueryBuilder.java Visa fil

@@ -0,0 +1,94 @@
package com.vaadin.data.util.query.generator.filter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.query.generator.StatementHelper;

public class QueryBuilder {

private static ArrayList<FilterTranslator> filterTranslators = new ArrayList<FilterTranslator>();
private static StringDecorator stringDecorator = new StringDecorator("\"",
"\"");

static {
/* Register all default filter translators */
addFilterTranslator(new AndTranslator());
addFilterTranslator(new OrTranslator());
addFilterTranslator(new LikeTranslator());
addFilterTranslator(new BetweenTranslator());
addFilterTranslator(new CompareTranslator());
addFilterTranslator(new NotTranslator());
addFilterTranslator(new IsNullTranslator());
addFilterTranslator(new SimpleStringTranslator());
}

public synchronized static void addFilterTranslator(
FilterTranslator translator) {
filterTranslators.add(translator);
}

/**
* Allows specification of a custom ColumnQuoter instance that handles
* quoting of column names for the current DB dialect.
*
* @param decorator
* the ColumnQuoter instance to use.
*/
public static void setStringDecorator(StringDecorator decorator) {
stringDecorator = decorator;
}

public static String quote(Object str) {
return stringDecorator.quote(str);
}

public static String group(String str) {
return stringDecorator.group(str);
}

/**
* Constructs and returns a string representing the filter that can be used
* in a WHERE clause.
*
* @param filter
* the filter to translate
* @param sh
* the statement helper to update with the value(s) of the filter
* @return a string representing the filter.
*/
public synchronized static String getWhereStringForFilter(Filter filter,
StatementHelper sh) {
for (FilterTranslator ft : filterTranslators) {
if (ft.translatesFilter(filter)) {
return ft.getWhereStringForFilter(filter, sh);
}
}
return "";
}

public static String getJoinedFilterString(Collection<Filter> filters,
String joinString, StatementHelper sh) {
StringBuilder result = new StringBuilder();
for (Filter f : filters) {
result.append(getWhereStringForFilter(f, sh));
result.append(" ").append(joinString).append(" ");
}
// Remove the last instance of joinString
result.delete(result.length() - joinString.length() - 2,
result.length());
return result.toString();
}

public static String getWhereStringForFilters(List<Filter> filters,
StatementHelper sh) {
if (filters == null || filters.isEmpty()) {
return "";
}
StringBuilder where = new StringBuilder(" WHERE ");
where.append(getJoinedFilterString(filters, "AND", sh));
return where.toString();
}
}

+ 25
- 0
src/com/vaadin/data/util/query/generator/filter/SimpleStringTranslator.java Visa fil

@@ -0,0 +1,25 @@
package com.vaadin.data.util.query.generator.filter;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.Like;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.data.util.query.generator.StatementHelper;

public class SimpleStringTranslator implements FilterTranslator {

public boolean translatesFilter(Filter filter) {
return filter instanceof SimpleStringFilter;
}

public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
SimpleStringFilter ssf = (SimpleStringFilter) filter;
// Create a Like filter based on the SimpleStringFilter and execute the
// LikeTranslator
String likeStr = ssf.isOnlyMatchPrefix() ? ssf.getFilterString() + "%"
: "%" + ssf.getFilterString() + "%";
Like like = new Like(ssf.getPropertyId().toString(), likeStr);
like.setCaseSensitive(!ssf.isIgnoreCase());
return new LikeTranslator().getWhereStringForFilter(like, sh);
}

}

+ 53
- 0
src/com/vaadin/data/util/query/generator/filter/StringDecorator.java Visa fil

@@ -0,0 +1,53 @@
package com.vaadin.data.util.query.generator.filter;

/**
* The StringDecorator knows how to produce a quoted string using the specified
* quote start and quote end characters. It also handles grouping of a string
* (surrounding it in parenthesis).
*
* Extend this class if you need to support special characters for grouping
* (parenthesis).
*
* @author Vaadin Ltd
*/
public class StringDecorator {

private final String quoteStart;
private final String quoteEnd;

/**
* Constructs a StringDecorator that uses the quoteStart and quoteEnd
* characters to create quoted strings.
*
* @param quoteStart
* the character denoting the start of a quote.
* @param quoteEnd
* the character denoting the end of a quote.
*/
public StringDecorator(String quoteStart, String quoteEnd) {
this.quoteStart = quoteStart;
this.quoteEnd = quoteEnd;
}

/**
* Surround a string with quote characters.
*
* @param str
* the string to quote
* @return the quoted string
*/
public String quote(Object str) {
return quoteStart + str + quoteEnd;
}

/**
* Groups a string by surrounding it in parenthesis
*
* @param str
* the string to group
* @return the grouped string
*/
public String group(String str) {
return "(" + str + ")";
}
}

+ 191
- 0
tests/src/com/vaadin/tests/containers/sqlcontainer/CheckboxUpdateProblem.java Visa fil

@@ -0,0 +1,191 @@
package com.vaadin.tests.containers.sqlcontainer;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import com.vaadin.Application;
import com.vaadin.data.Container.ItemSetChangeEvent;
import com.vaadin.data.Container.ItemSetChangeListener;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.util.SQLContainer;
import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
import com.vaadin.data.util.query.TableQuery;
import com.vaadin.tests.server.container.sqlcontainer.AllTests;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Form;
import com.vaadin.ui.HorizontalSplitPanel;
import com.vaadin.ui.Table;
import com.vaadin.ui.Window;

public class CheckboxUpdateProblem extends Application implements
Property.ValueChangeListener {
private final DatabaseHelper databaseHelper = new DatabaseHelper();
private Table testList;
private final HorizontalSplitPanel horizontalSplit = new HorizontalSplitPanel();

private TestForm testForm = new TestForm();

@Override
public void init() {
setMainWindow(new Window("Test window"));
horizontalSplit.setSizeFull();
testList = new Table();

horizontalSplit.setFirstComponent(testList);
testList.setSizeFull();
testList.setContainerDataSource(databaseHelper.getTestContainer());
testList.setSelectable(true);
testList.setImmediate(true);
testList.addListener(this);

databaseHelper.getTestContainer().addListener(
new ItemSetChangeListener() {
public void containerItemSetChange(ItemSetChangeEvent event) {
Object selected = testList.getValue();
if (selected != null) {
testForm.setItemDataSource(testList
.getItem(selected));
}
}
});

testForm = new TestForm();
testForm.setItemDataSource(null);

horizontalSplit.setSecondComponent(testForm);

getMainWindow().setContent(horizontalSplit);
}

public void valueChange(ValueChangeEvent event) {

Property property = event.getProperty();
if (property == testList) {
Item item = testList.getItem(testList.getValue());

if (item != testForm.getItemDataSource()) {
testForm.setItemDataSource(item);
}
}

}

private class TestForm extends Form implements Button.ClickListener {

private final Button save;

private TestForm() {
setSizeFull();
setWriteThrough(false);
setInvalidCommitted(false);

save = new Button("Save", (ClickListener) this);
getFooter().addComponent(save);
getFooter().setVisible(false);
}

public void buttonClick(ClickEvent event) {
if (event.getSource() == save) {
super.commit();

try {
databaseHelper.getTestContainer().commit();
getMainWindow().showNotification("Saved");
} catch (SQLException e) {
e.printStackTrace();
}
}
}

@Override
public void setItemDataSource(Item newDataSource) {
super.setItemDataSource(newDataSource);

if (newDataSource != null) {
getFooter().setVisible(true);
} else {
getFooter().setVisible(false);
}
}

}

private class DatabaseHelper {

private JDBCConnectionPool connectionPool = null;
private SQLContainer testContainer = null;
private static final String TABLENAME = "testtable";

public DatabaseHelper() {
initConnectionPool();
initDatabase();
initContainers();
}

private void initDatabase() {
try {
Connection conn = connectionPool.reserveConnection();
Statement statement = conn.createStatement();
try {
statement.execute("drop table " + TABLENAME);
} catch (SQLException e) {
// Will fail if table doesn't exist, which is OK.
conn.rollback();
}
switch (AllTests.db) {
case MYSQL:
statement
.execute("create table "
+ TABLENAME
+ " (id integer auto_increment not null, field1 varchar(100), field2 boolean, primary key(id))");
break;
case POSTGRESQL:
statement
.execute("create table "
+ TABLENAME
+ " (\"id\" serial primary key, \"field1\" varchar(100), \"field2\" boolean)");
break;
}
statement.executeUpdate("insert into " + TABLENAME
+ " values(default, 'Kalle', 'true')");
statement.close();
conn.commit();
connectionPool.releaseConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}

private void initContainers() {
try {
TableQuery q1 = new TableQuery(TABLENAME, connectionPool);
q1.setVersionColumn("id");
testContainer = new SQLContainer(q1);
testContainer.setDebugMode(true);
} catch (SQLException e) {
e.printStackTrace();
}
}

private void initConnectionPool() {
try {
connectionPool = new SimpleJDBCConnectionPool(
AllTests.dbDriver, AllTests.dbURL, AllTests.dbUser,
AllTests.dbPwd, 2, 5);
} catch (SQLException e) {
e.printStackTrace();
}
}

public SQLContainer getTestContainer() {
return testContainer;
}
}

}

+ 134
- 0
tests/src/com/vaadin/tests/containers/sqlcontainer/MassInsertMemoryLeakTestApp.java Visa fil

@@ -0,0 +1,134 @@
package com.vaadin.tests.containers.sqlcontainer;

import java.sql.SQLException;

import com.vaadin.Application;
import com.vaadin.data.util.SQLContainer;
import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
import com.vaadin.data.util.query.TableQuery;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.ProgressIndicator;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;

// author table in testdb (MySQL) is set out as follows
// +-------------+-------------+------+-----+---------+----------------+
// | Field | Type | Null | Key | Default | Extra |
// +-------------+-------------+------+-----+---------+----------------+
// | id | int(11) | NO | PRI | NULL | auto_increment |
// | last_name | varchar(40) | NO | | NULL | |
// | first_names | varchar(80) | NO | | NULL | |
// +-------------+-------------+------+-----+---------+----------------+

@SuppressWarnings("serial")
public class MassInsertMemoryLeakTestApp extends Application {

ProgressIndicator proggress = new ProgressIndicator();
Button process = new Button("Mass insert");

@Override
public void init() {
setMainWindow(new Window("SQLContainer Test", buildLayout()));

process.addListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
MassInsert mi = new MassInsert();
mi.start();
}
});
}

private class MassInsert extends Thread {

@Override
public synchronized void start() {
proggress.setVisible(true);
proggress.setValue(new Float(0));
proggress.setPollingInterval(100);
process.setEnabled(false);
proggress.setCaption("");
super.start();
}

@Override
public void run() {
JDBCConnectionPool pool = getConnectionPool();
if (pool != null) {
try {
int cents = 100;
for (int cent = 0; cent < cents; cent++) {
TableQuery q = new TableQuery("AUTHOR", pool);
q.setVersionColumn("ID");
SQLContainer c = new SQLContainer(q);
for (int i = 0; i < 100; i++) {
Object id = c.addItem();
c.getContainerProperty(id, "FIRST_NAMES").setValue(
getRandonName());
c.getContainerProperty(id, "LAST_NAME").setValue(
getRandonName());
}
c.commit();
synchronized (MassInsertMemoryLeakTestApp.this) {
proggress
.setValue(new Float((1.0f * cent) / cents));
proggress.setCaption("" + 100 * cent
+ " rows inserted");
}
}
} catch (SQLException e) {
getMainWindow().showNotification(
"SQLException while processing",
e.getLocalizedMessage());
e.printStackTrace();
}
}
synchronized (MassInsertMemoryLeakTestApp.this) {
proggress.setVisible(false);
proggress.setPollingInterval(0);
process.setEnabled(true);
}
}
}

private ComponentContainer buildLayout() {
VerticalLayout lo = new VerticalLayout();
lo.setSizeFull();
lo.addComponent(proggress);
lo.addComponent(process);
lo.setComponentAlignment(proggress, Alignment.BOTTOM_CENTER);
lo.setComponentAlignment(process, Alignment.TOP_CENTER);
lo.setSpacing(true);
proggress.setIndeterminate(false);
proggress.setVisible(false);
return lo;
}

private String getRandonName() {
final String[] tokens = new String[] { "sa", "len", "da", "vid", "ma",
"ry", "an", "na", "jo", "bri", "son", "mat", "e", "ric", "ge",
"eu", "han", "har", "ri", "ja", "lo" };
StringBuffer sb = new StringBuffer();
int len = (int) (Math.random() * 3 + 2);
while (len-- > 0) {
sb.append(tokens[(int) (Math.random() * tokens.length)]);
}
return Character.toUpperCase(sb.charAt(0)) + sb.toString().substring(1);
}

private JDBCConnectionPool getConnectionPool() {
SimpleJDBCConnectionPool pool = null;
try {
pool = new SimpleJDBCConnectionPool("com.mysql.jdbc.Driver",
"jdbc:mysql://localhost:3306/sqlcontainer", "sqlcontainer",
"sqlcontainer");
} catch (SQLException e) {
getMainWindow().showNotification("Error connecting to database");
}
return pool;
}

}

+ 146
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/AllTests.java Visa fil

@@ -0,0 +1,146 @@
package com.vaadin.tests.server.container.sqlcontainer;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

import com.vaadin.data.util.query.generator.DefaultSQLGenerator;
import com.vaadin.data.util.query.generator.MSSQLGenerator;
import com.vaadin.data.util.query.generator.OracleGenerator;
import com.vaadin.data.util.query.generator.SQLGenerator;
import com.vaadin.tests.server.container.sqlcontainer.connection.J2EEConnectionPoolTest;
import com.vaadin.tests.server.container.sqlcontainer.connection.SimpleJDBCConnectionPoolTest;
import com.vaadin.tests.server.container.sqlcontainer.filters.BetweenTest;
import com.vaadin.tests.server.container.sqlcontainer.filters.LikeTest;
import com.vaadin.tests.server.container.sqlcontainer.generator.SQLGeneratorsTest;
import com.vaadin.tests.server.container.sqlcontainer.query.FreeformQueryTest;
import com.vaadin.tests.server.container.sqlcontainer.query.QueryBuilderTest;
import com.vaadin.tests.server.container.sqlcontainer.query.TableQueryTest;

@RunWith(Suite.class)
@SuiteClasses({ SimpleJDBCConnectionPoolTest.class,
J2EEConnectionPoolTest.class, LikeTest.class,
QueryBuilderTest.class, FreeformQueryTest.class,
RowIdTest.class, SQLContainerTest.class,
SQLContainerTableQueryTest.class, ColumnPropertyTest.class,
TableQueryTest.class, SQLGeneratorsTest.class, UtilTest.class,
TicketTests.class, BetweenTest.class, ReadOnlyRowIdTest.class})
public class AllTests {
/* Set the DB used for testing here! */
public enum DB {
HSQLDB, MYSQL, POSTGRESQL, MSSQL, ORACLE;
}

/* 0 = HSQLDB, 1 = MYSQL, 2 = POSTGRESQL, 3 = MSSQL, 4 = ORACLE */
public static final DB db = DB.HSQLDB;

/* Auto-increment column offset (HSQLDB = 0, MYSQL = 1, POSTGRES = 1) */
public static int offset;
/* Garbage table creation query (=three queries for oracle) */
public static String createGarbage;
public static String createGarbageSecond;
public static String createGarbageThird;
/* DB Drivers, urls, usernames and passwords */
public static String dbDriver;
public static String dbURL;
public static String dbUser;
public static String dbPwd;
/* People -test table creation statement(s) */
public static String peopleFirst;
public static String peopleSecond;
public static String peopleThird;
/* Versioned -test table createion statement(s) */
public static String[] versionStatements;
/* SQL Generator used during the testing */
public static SQLGenerator sqlGen;

/* Set DB-specific settings based on selected DB */
static {
sqlGen = new DefaultSQLGenerator();
switch (db) {
case HSQLDB:
offset = 0;
createGarbage = "create table garbage (id integer generated always as identity, type varchar(32), PRIMARY KEY(id))";
dbDriver = "org.hsqldb.jdbc.JDBCDriver";
dbURL = "jdbc:hsqldb:mem:sqlcontainer";
dbUser = "SA";
dbPwd = "";
peopleFirst = "create table people (id integer generated always as identity, name varchar(32), AGE INTEGER)";
peopleSecond = "alter table people add primary key (id)";
versionStatements = new String[] {
"create table versioned (id integer generated always as identity, text varchar(255), version tinyint default 0)",
"alter table versioned add primary key (id)" };
break;
case MYSQL:
offset = 1;
createGarbage = "create table GARBAGE (ID integer auto_increment, type varchar(32), PRIMARY KEY(ID))";
dbDriver = "com.mysql.jdbc.Driver";
dbURL = "jdbc:mysql:///sqlcontainer";
dbUser = "sqlcontainer";
dbPwd = "sqlcontainer";
peopleFirst = "create table PEOPLE (ID integer auto_increment not null, NAME varchar(32), AGE INTEGER, primary key(ID))";
peopleSecond = null;
versionStatements = new String[] {
"create table VERSIONED (ID integer auto_increment not null, TEXT varchar(255), VERSION tinyint default 0, primary key(ID))",
"CREATE TRIGGER upd_version BEFORE UPDATE ON VERSIONED"
+ " FOR EACH ROW SET NEW.VERSION = @VERSION+1" };
break;
case POSTGRESQL:
offset = 1;
createGarbage = "create table GARBAGE (\"ID\" serial PRIMARY KEY, \"TYPE\" varchar(32))";
dbDriver = "org.postgresql.Driver";
dbURL = "jdbc:postgresql://localhost:5432/test";
dbUser = "postgres";
dbPwd = "postgres";
peopleFirst = "create table PEOPLE (\"ID\" serial primary key, \"NAME\" VARCHAR(32), \"AGE\" INTEGER)";
peopleSecond = null;
versionStatements = new String[] {
"create table VERSIONED (\"ID\" serial primary key, \"TEXT\" VARCHAR(255), \"VERSION\" INTEGER DEFAULT 0)",
"CREATE OR REPLACE FUNCTION zz_row_version() RETURNS TRIGGER AS $$"
+ "BEGIN"
+ " IF TG_OP = 'UPDATE'"
+ " AND NEW.\"VERSION\" = old.\"VERSION\""
+ " AND ROW(NEW.*) IS DISTINCT FROM ROW (old.*)"
+ " THEN"
+ " NEW.\"VERSION\" := NEW.\"VERSION\" + 1;"
+ " END IF;" + " RETURN NEW;" + "END;"
+ "$$ LANGUAGE plpgsql;",
"CREATE TRIGGER \"mytable_modify_dt_tr\" BEFORE UPDATE"
+ " ON VERSIONED FOR EACH ROW"
+ " EXECUTE PROCEDURE \"public\".\"zz_row_version\"();" };
break;
case MSSQL:
offset = 1;
createGarbage = "create table GARBAGE (\"ID\" int identity(1,1) primary key, \"TYPE\" varchar(32))";
dbDriver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
dbURL = "jdbc:sqlserver://localhost:1433;databaseName=tempdb;";
dbUser = "sa";
dbPwd = "sa";
peopleFirst = "create table PEOPLE (\"ID\" int identity(1,1) primary key, \"NAME\" VARCHAR(32), \"AGE\" INTEGER)";
peopleSecond = null;
versionStatements = new String[] { "create table VERSIONED (\"ID\" int identity(1,1) primary key, \"TEXT\" VARCHAR(255), \"VERSION\" rowversion not null)" };
sqlGen = new MSSQLGenerator();
break;
case ORACLE:
offset = 1;
createGarbage = "create table GARBAGE (\"ID\" integer primary key, \"TYPE\" varchar2(32))";
createGarbageSecond = "create sequence garbage_seq start with 1 increment by 1 nomaxvalue";
createGarbageThird = "create trigger garbage_trigger before insert on GARBAGE for each row begin select garbage_seq.nextval into :new.ID from dual; end;";
dbDriver = "oracle.jdbc.OracleDriver";
dbURL = "jdbc:oracle:thin:test/test@localhost:1521:XE";
dbUser = "test";
dbPwd = "test";
peopleFirst = "create table PEOPLE (\"ID\" integer primary key, \"NAME\" VARCHAR2(32), \"AGE\" INTEGER)";
peopleSecond = "create sequence people_seq start with 1 increment by 1 nomaxvalue";
peopleThird = "create trigger people_trigger before insert on PEOPLE for each row begin select people_seq.nextval into :new.ID from dual; end;";
versionStatements = new String[] {
"create table VERSIONED (\"ID\" integer primary key, \"TEXT\" VARCHAR(255), \"VERSION\" INTEGER DEFAULT 0)",
"create sequence versioned_seq start with 1 increment by 1 nomaxvalue",
"create trigger versioned_trigger before insert on VERSIONED for each row begin select versioned_seq.nextval into :new.ID from dual; end;",
"create sequence versioned_version start with 1 increment by 1 nomaxvalue",
"create trigger versioned_version_trigger before insert or update on VERSIONED for each row begin select versioned_version.nextval into :new.VERSION from dual; end;" };
sqlGen = new OracleGenerator();
break;
}
}
}

+ 177
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/ColumnPropertyTest.java Visa fil

@@ -0,0 +1,177 @@
package com.vaadin.tests.server.container.sqlcontainer;

import java.util.Arrays;

import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;

import com.vaadin.data.Property.ReadOnlyException;
import com.vaadin.data.util.ColumnProperty;
import com.vaadin.data.util.ColumnProperty.NotNullableException;
import com.vaadin.data.util.RowId;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.SQLContainer;

public class ColumnPropertyTest {

@Test
public void constructor_legalParameters_shouldSucceed() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
Assert.assertNotNull(cp);
}

@Test(expected = IllegalArgumentException.class)
public void constructor_missingPropertyId_shouldFail() {
new ColumnProperty(null, false, true, true, "Ville", String.class);
}

@Test(expected = IllegalArgumentException.class)
public void constructor_missingType_shouldFail() {
new ColumnProperty("NAME", false, true, true, "Ville", null);
}

@Test
public void getValue_defaultValue_returnsVille() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
Assert.assertEquals("Ville", cp.getValue());
}

/*-
* TODO Removed test since currently the Vaadin test package structure
* does not allow testing protected methods. When it has been fixed
* then re-enable test.
@Test
public void setValue_readWriteNullable_returnsKalle() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
SQLContainer container = EasyMock.createMock(SQLContainer.class);
RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }),
Arrays.asList(cp));
container.itemChangeNotification(owner);
EasyMock.replay(container);
cp.setValue("Kalle");
Assert.assertEquals("Kalle", cp.getValue());
EasyMock.verify(container);
}
*/

@Test(expected = ReadOnlyException.class)
public void setValue_readOnlyNullable_shouldFail() {
ColumnProperty cp = new ColumnProperty("NAME", true, true, true,
"Ville", String.class);
SQLContainer container = EasyMock.createMock(SQLContainer.class);
new RowItem(container, new RowId(new Object[] { 1 }), Arrays.asList(cp));
EasyMock.replay(container);
cp.setValue("Kalle");
EasyMock.verify(container);
}

/*-
* TODO Removed test since currently the Vaadin test package structure
* does not allow testing protected methods. When it has been fixed
* then re-enable test.
@Test
public void setValue_readWriteNullable_nullShouldWork() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
SQLContainer container = EasyMock.createMock(SQLContainer.class);
RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }),
Arrays.asList(cp));
container.itemChangeNotification(owner);
EasyMock.replay(container);
cp.setValue(null);
Assert.assertNull(cp.getValue());
EasyMock.verify(container);
}

@Test(expected = NotNullableException.class)
public void setValue_readWriteNotNullable_nullShouldFail() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, false,
"Ville", String.class);
SQLContainer container = EasyMock.createMock(SQLContainer.class);
RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }),
Arrays.asList(cp));
container.itemChangeNotification(owner);
EasyMock.replay(container);
cp.setValue(null);
Assert.assertNotNull(cp.getValue());
EasyMock.verify(container);
}
*/

@Test
public void getType_normal_returnsStringClass() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
Assert.assertSame(String.class, cp.getType());
}

@Test
public void isReadOnly_readWriteNullable_returnsTrue() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
Assert.assertFalse(cp.isReadOnly());
}

@Test
public void isReadOnly_readOnlyNullable_returnsTrue() {
ColumnProperty cp = new ColumnProperty("NAME", true, true, true,
"Ville", String.class);
Assert.assertTrue(cp.isReadOnly());
}

@Test
public void setReadOnly_readOnlyChangeAllowed_shouldSucceed() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
cp.setReadOnly(true);
Assert.assertTrue(cp.isReadOnly());
}

@Test
public void setReadOnly_readOnlyChangeDisallowed_shouldFail() {
ColumnProperty cp = new ColumnProperty("NAME", false, false, true,
"Ville", String.class);
cp.setReadOnly(true);
Assert.assertFalse(cp.isReadOnly());
}

@Test
public void getPropertyId_normal_returnsNAME() {
ColumnProperty cp = new ColumnProperty("NAME", false, false, true,
"Ville", String.class);
Assert.assertEquals("NAME", cp.getPropertyId());
}

/*-
* TODO Removed test since currently the Vaadin test package structure
* does not allow testing protected methods. When it has been fixed
* then re-enable test.
@Test
public void isModified_valueModified_returnsTrue() {
ColumnProperty cp = new ColumnProperty("NAME", false, true, true,
"Ville", String.class);
SQLContainer container = EasyMock.createMock(SQLContainer.class);
RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }),
Arrays.asList(cp));
container.itemChangeNotification(owner);
EasyMock.replay(container);
cp.setValue("Kalle");
Assert.assertEquals("Kalle", cp.getValue());
Assert.assertTrue(cp.isModified());
EasyMock.verify(container);
}
*/

@Test
public void isModified_valueNotModified_returnsFalse() {
ColumnProperty cp = new ColumnProperty("NAME", false, false, true,
"Ville", String.class);
Assert.assertFalse(cp.isModified());
}

}

+ 132
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/DataGenerator.java Visa fil

@@ -0,0 +1,132 @@
package com.vaadin.tests.server.container.sqlcontainer;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.Assert;

import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;

public class DataGenerator {

public static void addPeopleToDatabase(JDBCConnectionPool connectionPool)
throws SQLException {
Connection conn = connectionPool.reserveConnection();
Statement statement = conn.createStatement();
try {
statement.execute("drop table PEOPLE");
if (AllTests.db == DB.ORACLE) {
statement.execute("drop sequence people_seq");
}
} catch (SQLException e) {
// Will fail if table doesn't exist, which is OK.
conn.rollback();
}
statement.execute(AllTests.peopleFirst);
if (AllTests.peopleSecond != null) {
statement.execute(AllTests.peopleSecond);
}
if (AllTests.db == DB.ORACLE) {
statement.execute(AllTests.peopleThird);
}
if (AllTests.db == DB.MSSQL) {
statement.executeUpdate("insert into people values('Ville', '23')");
statement.executeUpdate("insert into people values('Kalle', '7')");
statement.executeUpdate("insert into people values('Pelle', '18')");
statement.executeUpdate("insert into people values('Börje', '64')");
} else {
statement
.executeUpdate("insert into people values(default, 'Ville', '23')");
statement
.executeUpdate("insert into people values(default, 'Kalle', '7')");
statement
.executeUpdate("insert into people values(default, 'Pelle', '18')");
statement
.executeUpdate("insert into people values(default, 'Börje', '64')");
}
statement.close();
statement = conn.createStatement();
ResultSet rs = statement.executeQuery("select * from PEOPLE");
Assert.assertTrue(rs.next());
statement.close();
conn.commit();
connectionPool.releaseConnection(conn);
}

public static void addFiveThousandPeople(JDBCConnectionPool connectionPool)
throws SQLException {
Connection conn = connectionPool.reserveConnection();
Statement statement = conn.createStatement();
for (int i = 4; i < 5000; i++) {
if (AllTests.db == DB.MSSQL) {
statement.executeUpdate("insert into people values('Person "
+ i + "', '" + i % 99 + "')");
} else {
statement
.executeUpdate("insert into people values(default, 'Person "
+ i + "', '" + i % 99 + "')");
}
}
statement.close();
conn.commit();
connectionPool.releaseConnection(conn);
}

public static void addVersionedData(JDBCConnectionPool connectionPool)
throws SQLException {
Connection conn = connectionPool.reserveConnection();
Statement statement = conn.createStatement();
try {
statement.execute("DROP TABLE VERSIONED");
if (AllTests.db == DB.ORACLE) {
statement.execute("drop sequence versioned_seq");
statement.execute("drop sequence versioned_version");
}
} catch (SQLException e) {
// Will fail if table doesn't exist, which is OK.
conn.rollback();
}
for (String stmtString : AllTests.versionStatements) {
statement.execute(stmtString);
}
if (AllTests.db == DB.MSSQL) {
statement
.executeUpdate("insert into VERSIONED values('Junk', default)");
} else {
statement
.executeUpdate("insert into VERSIONED values(default, 'Junk', default)");
}
statement.close();
statement = conn.createStatement();
ResultSet rs = statement.executeQuery("select * from VERSIONED");
Assert.assertTrue(rs.next());
statement.close();
conn.commit();
connectionPool.releaseConnection(conn);
}

public static void createGarbage(JDBCConnectionPool connectionPool)
throws SQLException {
Connection conn = connectionPool.reserveConnection();
Statement statement = conn.createStatement();
try {
statement.execute("drop table GARBAGE");
if (AllTests.db == DB.ORACLE) {
statement.execute("drop sequence garbage_seq");
}
} catch (SQLException e) {
// Will fail if table doesn't exist, which is OK.
conn.rollback();
}
statement.execute(AllTests.createGarbage);
if (AllTests.db == DB.ORACLE) {
statement.execute(AllTests.createGarbageSecond);
statement.execute(AllTests.createGarbageThird);
}
conn.commit();
connectionPool.releaseConnection(conn);
}
}

+ 66
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/FreeformQueryUtil.java Visa fil

@@ -0,0 +1,66 @@
package com.vaadin.tests.server.container.sqlcontainer;

import java.util.List;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.query.generator.StatementHelper;
import com.vaadin.data.util.query.generator.filter.QueryBuilder;
import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;

public class FreeformQueryUtil {

public static StatementHelper getQueryWithFilters(List<Filter> filters,
int offset, int limit) {
StatementHelper sh = new StatementHelper();
if (AllTests.db == DB.MSSQL) {
if (limit > 1) {
offset++;
limit--;
}
StringBuilder query = new StringBuilder();
query.append("SELECT * FROM (SELECT row_number() OVER (");
query.append("ORDER BY \"ID\" ASC");
query.append(") AS rownum, * FROM \"PEOPLE\"");

if (!filters.isEmpty()) {
query.append(QueryBuilder.getWhereStringForFilters(
filters, sh));
}
query.append(") AS a WHERE a.rownum BETWEEN ").append(offset)
.append(" AND ").append(Integer.toString(offset + limit));
sh.setQueryString(query.toString());
return sh;
} else if (AllTests.db == DB.ORACLE) {
if (limit > 1) {
offset++;
limit--;
}
StringBuilder query = new StringBuilder();
query.append("SELECT * FROM (SELECT x.*, ROWNUM AS "
+ "\"rownum\" FROM (SELECT * FROM \"PEOPLE\"");
if (!filters.isEmpty()) {
query.append(QueryBuilder.getWhereStringForFilters(
filters, sh));
}
query.append(") x) WHERE \"rownum\" BETWEEN ? AND ?");
sh.addParameterValue(offset);
sh.addParameterValue(offset + limit);
sh.setQueryString(query.toString());
return sh;
} else {
StringBuilder query = new StringBuilder("SELECT * FROM people");
if (!filters.isEmpty()) {
query.append(QueryBuilder.getWhereStringForFilters(
filters, sh));
}
if (limit != 0 || offset != 0) {
query.append(" LIMIT ? OFFSET ?");
sh.addParameterValue(limit);
sh.addParameterValue(offset);
}
sh.setQueryString(query.toString());
return sh;
}
}

}

+ 50
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/ReadOnlyRowIdTest.java Visa fil

@@ -0,0 +1,50 @@
package com.vaadin.tests.server.container.sqlcontainer;

import junit.framework.Assert;

import org.junit.Test;

import com.vaadin.data.util.ReadOnlyRowId;

public class ReadOnlyRowIdTest {

@Test
public void getRowNum_shouldReturnRowNumGivenInConstructor() {
int rowNum = 1337;
ReadOnlyRowId rid = new ReadOnlyRowId(rowNum);
Assert.assertEquals(rowNum, rid.getRowNum());
}

@Test
public void hashCode_shouldBeEqualToHashCodeOfRowNum() {
int rowNum = 1337;
ReadOnlyRowId rid = new ReadOnlyRowId(rowNum);
Assert.assertEquals(Integer.valueOf(rowNum).hashCode(), rid.hashCode());
}

@Test
public void equals_compareWithNull_shouldBeFalse() {
ReadOnlyRowId rid = new ReadOnlyRowId(1337);
Assert.assertFalse(rid.equals(null));
}

@Test
public void equals_compareWithSameInstance_shouldBeTrue() {
ReadOnlyRowId rid = new ReadOnlyRowId(1337);
ReadOnlyRowId rid2 = rid;
Assert.assertTrue(rid.equals(rid2));
}

@Test
public void equals_compareWithOtherType_shouldBeFalse() {
ReadOnlyRowId rid = new ReadOnlyRowId(1337);
Assert.assertFalse(rid.equals(new Object()));
}

@Test
public void equals_compareWithOtherRowId_shouldBeFalse() {
ReadOnlyRowId rid = new ReadOnlyRowId(1337);
ReadOnlyRowId rid2 = new ReadOnlyRowId(42);
Assert.assertFalse(rid.equals(rid2));
}
}

+ 55
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/RowIdTest.java Visa fil

@@ -0,0 +1,55 @@
package com.vaadin.tests.server.container.sqlcontainer;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.data.util.RowId;

public class RowIdTest {

@Test
public void constructor_withArrayOfPrimaryKeyColumns_shouldSucceed() {
RowId id = new RowId(new Object[] { "id", "name" });
Assert.assertArrayEquals(new Object[] { "id", "name" }, id.getId());
}

@Test(expected = IllegalArgumentException.class)
public void constructor_withNullParameter_shouldFail() {
new RowId(null);
}

@Test
public void hashCode_samePrimaryKeys_sameResult() {
RowId id = new RowId(new Object[] { "id", "name" });
RowId id2 = new RowId(new Object[] { "id", "name" });
Assert.assertEquals(id.hashCode(), id2.hashCode());
}

@Test
public void hashCode_differentPrimaryKeys_differentResult() {
RowId id = new RowId(new Object[] { "id", "name" });
RowId id2 = new RowId(new Object[] { "id" });
Assert.assertFalse(id.hashCode() == id2.hashCode());
}

@Test
public void equals_samePrimaryKeys_returnsTrue() {
RowId id = new RowId(new Object[] { "id", "name" });
RowId id2 = new RowId(new Object[] { "id", "name" });
Assert.assertEquals(id, id2);
}

@Test
public void equals_differentPrimaryKeys_returnsFalse() {
RowId id = new RowId(new Object[] { "id", "name" });
RowId id2 = new RowId(new Object[] { "id" });
Assert.assertFalse(id.equals(id2.hashCode()));
}

@Test
public void equals_differentDataType_returnsFalse() {
RowId id = new RowId(new Object[] { "id", "name" });
Assert.assertFalse(id.equals("Tudiluu"));
Assert.assertFalse(id.equals(new Integer(1337)));
}
}

+ 1501
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/SQLContainerTableQueryTest.java
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 2400
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/SQLContainerTest.java
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 157
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/TicketTests.java Visa fil

@@ -0,0 +1,157 @@
package com.vaadin.tests.server.container.sqlcontainer;

import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.SQLContainer;
import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
import com.vaadin.data.util.filter.Compare.Equal;
import com.vaadin.data.util.query.FreeformQuery;
import com.vaadin.data.util.query.FreeformStatementDelegate;
import com.vaadin.data.util.query.TableQuery;
import com.vaadin.data.util.query.generator.StatementHelper;
import com.vaadin.data.util.query.generator.filter.QueryBuilder;
import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
import com.vaadin.ui.Table;
import com.vaadin.ui.Window;

public class TicketTests {

private SimpleJDBCConnectionPool connectionPool;

@Before
public void setUp() throws SQLException {
connectionPool = new SimpleJDBCConnectionPool(AllTests.dbDriver,
AllTests.dbURL, AllTests.dbUser, AllTests.dbPwd, 2, 2);
DataGenerator.addPeopleToDatabase(connectionPool);
}

@Test
public void ticket5867_throwsIllegalState_transactionAlreadyActive()
throws SQLException {
SQLContainer container = new SQLContainer(new FreeformQuery(
"SELECT * FROM people", Arrays.asList("ID"), connectionPool));
Table table = new Table();
Window w = new Window();
w.addComponent(table);
table.setContainerDataSource(container);
}

@SuppressWarnings("unchecked")
@Test
public void ticket6136_freeform_ageIs18() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformStatementDelegate delegate = EasyMock
.createMock(FreeformStatementDelegate.class);
final ArrayList<Filter> filters = new ArrayList<Filter>();
delegate.setFilters(null);
EasyMock.expectLastCall().anyTimes();
delegate.setOrderBy(EasyMock.isA(List.class));
EasyMock.expectLastCall().anyTimes();
delegate.setOrderBy(null);
EasyMock.expectLastCall().anyTimes();
delegate.setFilters(EasyMock.isA(List.class));
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
public Object answer() throws Throwable {
List<Filter> orders = (List<Filter>) EasyMock
.getCurrentArguments()[0];
filters.clear();
filters.addAll(orders);
return null;
}
}).anyTimes();
EasyMock.expect(
delegate.getQueryStatement(EasyMock.anyInt(), EasyMock.anyInt()))
.andAnswer(new IAnswer<StatementHelper>() {
public StatementHelper answer() throws Throwable {
Object[] args = EasyMock.getCurrentArguments();
int offset = (Integer) (args[0]);
int limit = (Integer) (args[1]);
return FreeformQueryUtil.getQueryWithFilters(filters,
offset, limit);
}
}).anyTimes();
EasyMock.expect(delegate.getCountStatement())
.andAnswer(new IAnswer<StatementHelper>() {
@SuppressWarnings("deprecation")
public StatementHelper answer() throws Throwable {
StatementHelper sh = new StatementHelper();
StringBuffer query = new StringBuffer(
"SELECT COUNT(*) FROM people");
if (!filters.isEmpty()) {
query.append(QueryBuilder.getWhereStringForFilters(
filters, sh));
}
sh.setQueryString(query.toString());
return sh;
}
}).anyTimes();

EasyMock.replay(delegate);
query.setDelegate(delegate);
SQLContainer container = new SQLContainer(query);
// Ville, Kalle, Pelle, Börje
Assert.assertEquals(4, container.size());
Assert.assertEquals("Börje",
container.getContainerProperty(container.lastItemId(), "NAME")
.getValue());

container.addContainerFilter(new Equal("AGE", 18));
// Pelle
Assert.assertEquals(1, container.size());
Assert.assertEquals("Pelle",
container.getContainerProperty(container.firstItemId(), "NAME")
.getValue());
if (AllTests.db == DB.ORACLE) {
Assert.assertEquals(new BigDecimal(18), container
.getContainerProperty(container.firstItemId(), "AGE")
.getValue());
} else {
Assert.assertEquals(
18,
container.getContainerProperty(container.firstItemId(),
"AGE").getValue());
}

EasyMock.verify(delegate);
}

@Test
public void ticket6136_table_ageIs18() throws SQLException {
TableQuery query = new TableQuery("people", connectionPool,
AllTests.sqlGen);
SQLContainer container = new SQLContainer(query);
// Ville, Kalle, Pelle, Börje
Assert.assertEquals(4, container.size());

container.addContainerFilter(new Equal("AGE", 18));

// Pelle
Assert.assertEquals(1, container.size());
Assert.assertEquals("Pelle",
container.getContainerProperty(container.firstItemId(), "NAME")
.getValue());
if (AllTests.db == DB.ORACLE) {
Assert.assertEquals(new BigDecimal(18), container
.getContainerProperty(container.firstItemId(), "AGE")
.getValue());
} else {
Assert.assertEquals(
18,
container.getContainerProperty(container.firstItemId(),
"AGE").getValue());
}
}

}

+ 52
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/UtilTest.java Visa fil

@@ -0,0 +1,52 @@
package com.vaadin.tests.server.container.sqlcontainer;

import junit.framework.Assert;

import org.junit.Test;

import com.vaadin.data.util.SQLUtil;

public class UtilTest {

@Test
public void escapeSQL_noQuotes_returnsSameString() {
Assert.assertEquals("asdf", SQLUtil.escapeSQL("asdf"));
}

@Test
public void escapeSQL_singleQuotes_returnsEscapedString() {
Assert.assertEquals("O''Brien", SQLUtil.escapeSQL("O'Brien"));
}

@Test
public void escapeSQL_severalQuotes_returnsEscapedString() {
Assert.assertEquals("asdf''ghjk''qwerty",
SQLUtil.escapeSQL("asdf'ghjk'qwerty"));
}

@Test
public void escapeSQL_doubleQuotes_returnsEscapedString() {
Assert.assertEquals("asdf\\\"foo", SQLUtil.escapeSQL("asdf\"foo"));
}

@Test
public void escapeSQL_multipleDoubleQuotes_returnsEscapedString() {
Assert.assertEquals("asdf\\\"foo\\\"bar",
SQLUtil.escapeSQL("asdf\"foo\"bar"));
}

@Test
public void escapeSQL_backslashes_returnsEscapedString() {
Assert.assertEquals("foo\\\\nbar\\\\r", SQLUtil.escapeSQL("foo\\nbar\\r"));
}

@Test
public void escapeSQL_x00_removesX00() {
Assert.assertEquals("foobar", SQLUtil.escapeSQL("foo\\x00bar"));
}

@Test
public void escapeSQL_x1a_removesX1a() {
Assert.assertEquals("foobar", SQLUtil.escapeSQL("foo\\x1abar"));
}
}

+ 101
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/J2EEConnectionPoolTest.java Visa fil

@@ -0,0 +1,101 @@
package com.vaadin.tests.server.container.sqlcontainer.connection;

import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.sql.DataSource;

import junit.framework.Assert;

import org.easymock.EasyMock;
import org.junit.Test;

import com.vaadin.data.util.connection.J2EEConnectionPool;

public class J2EEConnectionPoolTest {

@Test
public void reserveConnection_dataSourceSpecified_shouldReturnValidConnection()
throws SQLException {
Connection connection = EasyMock.createMock(Connection.class);
connection.setAutoCommit(false);
EasyMock.expectLastCall();
DataSource ds = EasyMock.createMock(DataSource.class);
ds.getConnection();
EasyMock.expectLastCall().andReturn(connection);
EasyMock.replay(connection, ds);

J2EEConnectionPool pool = new J2EEConnectionPool(ds);
Connection c = pool.reserveConnection();
Assert.assertEquals(connection, c);
EasyMock.verify(connection, ds);
}

@Test
public void releaseConnection_shouldCloseConnection() throws SQLException {
Connection connection = EasyMock.createMock(Connection.class);
connection.setAutoCommit(false);
EasyMock.expectLastCall();
connection.close();
EasyMock.expectLastCall();
DataSource ds = EasyMock.createMock(DataSource.class);
ds.getConnection();
EasyMock.expectLastCall().andReturn(connection);
EasyMock.replay(connection, ds);

J2EEConnectionPool pool = new J2EEConnectionPool(ds);
Connection c = pool.reserveConnection();
Assert.assertEquals(connection, c);
pool.releaseConnection(c);
EasyMock.verify(connection, ds);
}

@Test
public void reserveConnection_dataSourceLookedUp_shouldReturnValidConnection()
throws SQLException, NamingException {
Connection connection = EasyMock.createMock(Connection.class);
connection.setAutoCommit(false);
EasyMock.expectLastCall();
connection.close();
EasyMock.expectLastCall();

DataSource ds = EasyMock.createMock(DataSource.class);
ds.getConnection();
EasyMock.expectLastCall().andReturn(connection);

System.setProperty("java.naming.factory.initial",
"com.vaadin.tests.server.container.sqlcontainer.connection.MockInitialContextFactory");
Context context = EasyMock.createMock(Context.class);
context.lookup("testDataSource");
EasyMock.expectLastCall().andReturn(ds);
MockInitialContextFactory.setMockContext(context);

EasyMock.replay(context, connection, ds);

J2EEConnectionPool pool = new J2EEConnectionPool("testDataSource");
Connection c = pool.reserveConnection();
Assert.assertEquals(connection, c);
pool.releaseConnection(c);
EasyMock.verify(context, connection, ds);
}

@Test(expected = SQLException.class)
public void reserveConnection_nonExistantDataSourceLookedUp_shouldFail()
throws SQLException, NamingException {
System.setProperty("java.naming.factory.initial",
"com.vaadin.addon.sqlcontainer.connection.MockInitialContextFactory");
Context context = EasyMock.createMock(Context.class);
context.lookup("foo");
EasyMock.expectLastCall().andThrow(new NamingException("fail"));
MockInitialContextFactory.setMockContext(context);

EasyMock.replay(context);

J2EEConnectionPool pool = new J2EEConnectionPool("foo");
Connection c = pool.reserveConnection();
EasyMock.verify(context);
}

}

+ 24
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/MockInitialContextFactory.java Visa fil

@@ -0,0 +1,24 @@
package com.vaadin.tests.server.container.sqlcontainer.connection;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;

/**
* Provides a JNDI initial context factory for the MockContext.
*/
public class MockInitialContextFactory implements InitialContextFactory {
private static Context mockCtx = null;

public static void setMockContext(Context ctx) {
mockCtx = ctx;
}

public Context getInitialContext(java.util.Hashtable<?, ?> environment)
throws NamingException {
if (mockCtx == null) {
throw new IllegalStateException("mock context was not set.");
}
return mockCtx;
}
}

+ 172
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java Visa fil

@@ -0,0 +1,172 @@
package com.vaadin.tests.server.container.sqlcontainer.connection;

import java.sql.Connection;
import java.sql.SQLException;

import junit.framework.Assert;

import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
import com.vaadin.tests.server.container.sqlcontainer.AllTests;

public class SimpleJDBCConnectionPoolTest {
private JDBCConnectionPool connectionPool;

@Before
public void setUp() throws SQLException {
connectionPool = new SimpleJDBCConnectionPool(AllTests.dbDriver,
AllTests.dbURL, AllTests.dbUser, AllTests.dbPwd, 2, 2);
}

@Test
public void reserveConnection_reserveNewConnection_returnsConnection()
throws SQLException {
Connection conn = connectionPool.reserveConnection();
Assert.assertNotNull(conn);
}

@Test
public void releaseConnection_releaseUnused_shouldNotThrowException()
throws SQLException {
Connection conn = connectionPool.reserveConnection();
connectionPool.releaseConnection(conn);
Assert.assertFalse(conn.isClosed());
}

@Test(expected = SQLException.class)
public void reserveConnection_noConnectionsLeft_shouldFail()
throws SQLException {
try {
connectionPool.reserveConnection();
connectionPool.reserveConnection();
} catch (SQLException e) {
e.printStackTrace();
Assert.fail("Exception before all connections used! "
+ e.getMessage());
}

connectionPool.reserveConnection();
Assert.fail("Reserving connection didn't fail even though no connections are available!");
}

@Test
public void reserveConnection_oneConnectionLeft_returnsConnection()
throws SQLException {
try {
connectionPool.reserveConnection();
} catch (SQLException e) {
e.printStackTrace();
Assert.fail("Exception before all connections used! "
+ e.getMessage());
}

Connection conn = connectionPool.reserveConnection();
Assert.assertNotNull(conn);
}

@Test
public void reserveConnection_oneConnectionJustReleased_returnsConnection()
throws SQLException {
Connection conn2 = null;
try {
connectionPool.reserveConnection();
conn2 = connectionPool.reserveConnection();
} catch (SQLException e) {
e.printStackTrace();
Assert.fail("Exception before all connections used! "
+ e.getMessage());
}

connectionPool.releaseConnection(conn2);

connectionPool.reserveConnection();
}

@Test(expected = IllegalArgumentException.class)
public void construct_allParametersNull_shouldFail() throws SQLException {
SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool(null, null,
null, null);
}

@Test(expected = IllegalArgumentException.class)
public void construct_onlyDriverNameGiven_shouldFail() throws SQLException {
SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool(
AllTests.dbDriver, null, null, null);
}

@Test(expected = IllegalArgumentException.class)
public void construct_onlyDriverNameAndUrlGiven_shouldFail()
throws SQLException {
SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool(
AllTests.dbDriver, AllTests.dbURL, null, null);
}

@Test(expected = IllegalArgumentException.class)
public void construct_onlyDriverNameAndUrlAndUserGiven_shouldFail()
throws SQLException {
SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool(
AllTests.dbDriver, AllTests.dbURL, AllTests.dbUser, null);
}

@Test(expected = RuntimeException.class)
public void construct_nonExistingDriver_shouldFail() throws SQLException {
SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool("foo",
AllTests.dbURL, AllTests.dbUser, AllTests.dbPwd);
}

@Test
public void reserveConnection_newConnectionOpened_shouldSucceed()
throws SQLException {
connectionPool = new SimpleJDBCConnectionPool(AllTests.dbDriver,
AllTests.dbURL, AllTests.dbUser, AllTests.dbPwd, 0, 2);
Connection c = connectionPool.reserveConnection();
Assert.assertNotNull(c);
}

@Test
public void releaseConnection_nullConnection_shouldDoNothing() {
connectionPool.releaseConnection(null);
}

@Test
public void releaseConnection_failingRollback_shouldCallClose()
throws SQLException {
Connection c = EasyMock.createMock(Connection.class);
c.getAutoCommit();
EasyMock.expectLastCall().andReturn(false);
c.rollback();
EasyMock.expectLastCall().andThrow(new SQLException("Rollback failed"));
c.close();
EasyMock.expectLastCall().atLeastOnce();
EasyMock.replay(c);
// make sure the connection pool is initialized
connectionPool.reserveConnection();
connectionPool.releaseConnection(c);
EasyMock.verify(c);
}

@Test
public void destroy_shouldCloseAllConnections() throws SQLException {
Connection c1 = connectionPool.reserveConnection();
Connection c2 = connectionPool.reserveConnection();
connectionPool.destroy();
Assert.assertTrue(c1.isClosed());
Assert.assertTrue(c2.isClosed());
}

@Test
public void destroy_shouldCloseAllConnections2() throws SQLException {
Connection c1 = connectionPool.reserveConnection();
Connection c2 = connectionPool.reserveConnection();
connectionPool.releaseConnection(c1);
connectionPool.releaseConnection(c2);
connectionPool.destroy();
Assert.assertTrue(c1.isClosed());
Assert.assertTrue(c2.isClosed());
}

}

+ 122
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/filters/BetweenTest.java Visa fil

@@ -0,0 +1,122 @@
package com.vaadin.tests.server.container.sqlcontainer.filters;

import junit.framework.Assert;

import org.easymock.EasyMock;
import org.junit.Test;

import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.util.filter.Between;

public class BetweenTest {

private Item itemWithPropertyValue(Object propertyId, Object value) {
Property property = EasyMock.createMock(Property.class);
property.getValue();
EasyMock.expectLastCall().andReturn(value).anyTimes();
EasyMock.replay(property);

Item item = EasyMock.createMock(Item.class);
item.getItemProperty(propertyId);
EasyMock.expectLastCall().andReturn(property).anyTimes();
EasyMock.replay(item);
return item;
}

@Test
public void passesFilter_valueIsInRange_shouldBeTrue() {
Item item = itemWithPropertyValue("foo", 15);
Between between = new Between("foo", 1, 30);
Assert.assertTrue(between.passesFilter("foo", item));
}

@Test
public void passesFilter_valueIsOutOfRange_shouldBeFalse() {
Item item = itemWithPropertyValue("foo", 15);
Between between = new Between("foo", 0, 2);
Assert.assertFalse(between.passesFilter("foo", item));
}

@Test
public void passesFilter_valueNotComparable_shouldBeFalse() {
Item item = itemWithPropertyValue("foo", new Object());
Between between = new Between("foo", 0, 2);
Assert.assertFalse(between.passesFilter("foo", item));
}

@Test
public void appliesToProperty_differentProperties_shoudlBeFalse() {
Between between = new Between("foo", 0, 2);
Assert.assertFalse(between.appliesToProperty("bar"));
}

@Test
public void appliesToProperty_sameProperties_shouldBeTrue() {
Between between = new Between("foo", 0, 2);
Assert.assertTrue(between.appliesToProperty("foo"));
}

@Test
public void hashCode_equalInstances_shouldBeEqual() {
Between b1 = new Between("foo", 0, 2);
Between b2 = new Between("foo", 0, 2);
Assert.assertEquals(b1.hashCode(), b2.hashCode());
}

@Test
public void equals_differentObjects_shouldBeFalse() {
Between b1 = new Between("foo", 0, 2);
Object obj = new Object();
Assert.assertFalse(b1.equals(obj));
}

@Test
public void equals_sameInstance_shouldBeTrue() {
Between b1 = new Between("foo", 0, 2);
Between b2 = b1;
Assert.assertTrue(b1.equals(b2));
}

@Test
public void equals_equalInstances_shouldBeTrue() {
Between b1 = new Between("foo", 0, 2);
Between b2 = new Between("foo", 0, 2);
Assert.assertTrue(b1.equals(b2));
}

@Test
public void equals_equalInstances2_shouldBeTrue() {
Between b1 = new Between(null, null, null);
Between b2 = new Between(null, null, null);
Assert.assertTrue(b1.equals(b2));
}

@Test
public void equals_secondValueDiffers_shouldBeFalse() {
Between b1 = new Between("foo", 0, 1);
Between b2 = new Between("foo", 0, 2);
Assert.assertFalse(b1.equals(b2));
}

@Test
public void equals_firstAndSecondValueDiffers_shouldBeFalse() {
Between b1 = new Between("foo", 0, null);
Between b2 = new Between("foo", 1, 2);
Assert.assertFalse(b1.equals(b2));
}

@Test
public void equals_propertyAndFirstAndSecondValueDiffers_shouldBeFalse() {
Between b1 = new Between("foo", null, 1);
Between b2 = new Between("bar", 1, 2);
Assert.assertFalse(b1.equals(b2));
}

@Test
public void equals_propertiesDiffer_shouldBeFalse() {
Between b1 = new Between(null, 0, 1);
Between b2 = new Between("bar", 0, 1);
Assert.assertFalse(b1.equals(b2));
}
}

+ 229
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/filters/LikeTest.java Visa fil

@@ -0,0 +1,229 @@
package com.vaadin.tests.server.container.sqlcontainer.filters;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.data.Item;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.data.util.filter.Like;

public class LikeTest {

@Test
public void passesFilter_valueIsNotStringType_shouldFail() {
Like like = new Like("test", "%foo%");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<Integer>(5));

Assert.assertFalse(like.passesFilter("id", item));
}

@Test
public void passesFilter_containsLikeQueryOnStringContainingValue_shouldSucceed() {
Like like = new Like("test", "%foo%");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("asdfooghij"));

Assert.assertTrue(like.passesFilter("id", item));
}

@Test
public void passesFilter_containsLikeQueryOnStringContainingValueCaseInsensitive_shouldSucceed() {
Like like = new Like("test", "%foo%");
like.setCaseSensitive(false);

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("asdfOOghij"));

Assert.assertTrue(like.passesFilter("id", item));
}

@Test
public void passesFilter_containsLikeQueryOnStringContainingValueConstructedCaseInsensitive_shouldSucceed() {
Like like = new Like("test", "%foo%", false);

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("asdfOOghij"));

Assert.assertTrue(like.passesFilter("id", item));
}

@Test
public void passesFilter_containsLikeQueryOnStringNotContainingValue_shouldFail() {
Like like = new Like("test", "%foo%");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("asdbarghij"));

Assert.assertFalse(like.passesFilter("id", item));
}

@Test
public void passesFilter_containsLikeQueryOnStringExactlyEqualToValue_shouldSucceed() {
Like like = new Like("test", "%foo%");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("foo"));

Assert.assertTrue(like.passesFilter("id", item));
}

@Test
public void passesFilter_containsLikeQueryOnStringEqualToValueMinusOneCharAtTheEnd_shouldFail() {
Like like = new Like("test", "%foo%");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("fo"));

Assert.assertFalse(like.passesFilter("id", item));
}

@Test
public void passesFilter_beginsWithLikeQueryOnStringBeginningWithValue_shouldSucceed() {
Like like = new Like("test", "foo%");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("foobar"));

Assert.assertTrue(like.passesFilter("id", item));
}

@Test
public void passesFilter_beginsWithLikeQueryOnStringNotBeginningWithValue_shouldFail() {
Like like = new Like("test", "foo%");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("barfoo"));

Assert.assertFalse(like.passesFilter("id", item));
}

@Test
public void passesFilter_endsWithLikeQueryOnStringEndingWithValue_shouldSucceed() {
Like like = new Like("test", "%foo");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("barfoo"));

Assert.assertTrue(like.passesFilter("id", item));
}

@Test
public void passesFilter_endsWithLikeQueryOnStringNotEndingWithValue_shouldFail() {
Like like = new Like("test", "%foo");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("foobar"));

Assert.assertFalse(like.passesFilter("id", item));
}

@Test
public void passesFilter_startsWithAndEndsWithOnMatchingValue_shouldSucceed() {
Like like = new Like("test", "foo%bar");

Item item = new PropertysetItem();
item.addItemProperty("test", new ObjectProperty<String>("fooASDFbar"));

Assert.assertTrue(like.passesFilter("id", item));
}

@Test
public void appliesToProperty_valueIsProperty_shouldBeTrue() {
Like like = new Like("test", "%foo");
Assert.assertTrue(like.appliesToProperty("test"));
}

@Test
public void appliesToProperty_valueIsNotProperty_shouldBeFalse() {
Like like = new Like("test", "%foo");
Assert.assertFalse(like.appliesToProperty("bar"));
}

@Test
public void equals_sameInstances_shouldBeTrue() {
Like like1 = new Like("test", "%foo");
Like like2 = like1;
Assert.assertTrue(like1.equals(like2));
}

@Test
public void equals_twoEqualInstances_shouldBeTrue() {
Like like1 = new Like("test", "foo");
Like like2 = new Like("test", "foo");
Assert.assertTrue(like1.equals(like2));
}

@Test
public void equals_differentValues_shouldBeFalse() {
Like like1 = new Like("test", "foo");
Like like2 = new Like("test", "bar");
Assert.assertFalse(like1.equals(like2));
}

@Test
public void equals_differentProperties_shouldBeFalse() {
Like like1 = new Like("foo", "test");
Like like2 = new Like("bar", "test");
Assert.assertFalse(like1.equals(like2));
}

@Test
public void equals_differentPropertiesAndValues_shouldBeFalse() {
Like like1 = new Like("foo", "bar");
Like like2 = new Like("baz", "zomg");
Assert.assertFalse(like1.equals(like2));
}

@Test
public void equals_differentClasses_shouldBeFalse() {
Like like1 = new Like("foo", "bar");
Object obj = new Object();
Assert.assertFalse(like1.equals(obj));
}

@Test
public void equals_bothHaveNullProperties_shouldBeTrue() {
Like like1 = new Like(null, "foo");
Like like2 = new Like(null, "foo");
Assert.assertTrue(like1.equals(like2));
}

@Test
public void equals_bothHaveNullValues_shouldBeTrue() {
Like like1 = new Like("foo", null);
Like like2 = new Like("foo", null);
Assert.assertTrue(like1.equals(like2));
}

@Test
public void equals_onePropertyIsNull_shouldBeFalse() {
Like like1 = new Like(null, "bar");
Like like2 = new Like("foo", "baz");
Assert.assertFalse(like1.equals(like2));
}

@Test
public void equals_oneValueIsNull_shouldBeFalse() {
Like like1 = new Like("foo", null);
Like like2 = new Like("baz", "bar");
Assert.assertFalse(like1.equals(like2));
}

@Test
public void hashCode_equalInstances_shouldBeEqual() {
Like like1 = new Like("test", "foo");
Like like2 = new Like("test", "foo");
Assert.assertEquals(like1.hashCode(), like2.hashCode());
}

@Test
public void hashCode_differentPropertiesAndValues_shouldNotEqual() {
Like like1 = new Like("foo", "bar");
Like like2 = new Like("baz", "zomg");
Assert.assertTrue(like1.hashCode() != like2.hashCode());
}
}

+ 241
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/generator/SQLGeneratorsTest.java Visa fil

@@ -0,0 +1,241 @@
package com.vaadin.tests.server.container.sqlcontainer.generator;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.SQLContainer;
import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
import com.vaadin.data.util.filter.Like;
import com.vaadin.data.util.filter.Or;
import com.vaadin.data.util.query.OrderBy;
import com.vaadin.data.util.query.TableQuery;
import com.vaadin.data.util.query.generator.DefaultSQLGenerator;
import com.vaadin.data.util.query.generator.MSSQLGenerator;
import com.vaadin.data.util.query.generator.OracleGenerator;
import com.vaadin.data.util.query.generator.SQLGenerator;
import com.vaadin.data.util.query.generator.StatementHelper;
import com.vaadin.tests.server.container.sqlcontainer.AllTests;
import com.vaadin.tests.server.container.sqlcontainer.DataGenerator;
public class SQLGeneratorsTest {
private JDBCConnectionPool connectionPool;
@Before
public void setUp() throws SQLException {
try {
connectionPool = new SimpleJDBCConnectionPool(AllTests.dbDriver,
AllTests.dbURL, AllTests.dbUser, AllTests.dbPwd, 2, 2);
} catch (SQLException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
DataGenerator.addPeopleToDatabase(connectionPool);
}
@After
public void tearDown() {
if (connectionPool != null) {
connectionPool.destroy();
}
}
@Test
public void generateSelectQuery_basicQuery_shouldSucceed() {
SQLGenerator sg = new DefaultSQLGenerator();
StatementHelper sh = sg.generateSelectQuery("TABLE", null, null, 0, 0,
null);
Assert.assertEquals(sh.getQueryString(), "SELECT * FROM TABLE");
}
@Test
public void generateSelectQuery_pagingAndColumnsSet_shouldSucceed() {
SQLGenerator sg = new DefaultSQLGenerator();
StatementHelper sh = sg.generateSelectQuery("TABLE", null, null, 4, 8,
"COL1, COL2, COL3");
Assert.assertEquals(sh.getQueryString(),
"SELECT COL1, COL2, COL3 FROM TABLE LIMIT 8 OFFSET 4");
}
/**
* Note: Only tests one kind of filter and ordering.
*/
@Test
public void generateSelectQuery_filtersAndOrderingSet_shouldSucceed() {
SQLGenerator sg = new DefaultSQLGenerator();
List<com.vaadin.data.Container.Filter> f = new ArrayList<Filter>();
f.add(new Like("name", "%lle"));
List<OrderBy> ob = Arrays.asList(new OrderBy("name", true));
StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 0, 0, null);
Assert.assertEquals(sh.getQueryString(),
"SELECT * FROM TABLE WHERE \"name\" LIKE ? ORDER BY \"name\" ASC");
}
@Test
public void generateSelectQuery_filtersAndOrderingSet_exclusiveFilteringMode_shouldSucceed() {
SQLGenerator sg = new DefaultSQLGenerator();
List<Filter> f = new ArrayList<Filter>();
f.add(new Or(new Like("name", "%lle"), new Like("name", "vi%")));
List<OrderBy> ob = Arrays.asList(new OrderBy("name", true));
StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 0, 0, null);
// TODO
Assert.assertEquals(sh.getQueryString(),
"SELECT * FROM TABLE WHERE (\"name\" LIKE ? "
+ "OR \"name\" LIKE ?) ORDER BY \"name\" ASC");
}
@Test
public void generateDeleteQuery_basicQuery_shouldSucceed()
throws SQLException {
/*
* No need to run this for Oracle/MSSQL generators since the
* DefaultSQLGenerator method would be called anyway.
*/
if (AllTests.sqlGen instanceof MSSQLGenerator
|| AllTests.sqlGen instanceof OracleGenerator) {
return;
}
SQLGenerator sg = AllTests.sqlGen;
TableQuery query = new TableQuery("people", connectionPool,
AllTests.sqlGen);
SQLContainer container = new SQLContainer(query);
StatementHelper sh = sg.generateDeleteQuery(
"people",
query.getPrimaryKeyColumns(),
null,
(RowItem) container.getItem(container.getItemIds().iterator()
.next()));
Assert.assertEquals("DELETE FROM people WHERE \"ID\" = ?",
sh.getQueryString());
}
@Test
public void generateUpdateQuery_basicQuery_shouldSucceed()
throws SQLException {
/*
* No need to run this for Oracle/MSSQL generators since the
* DefaultSQLGenerator method would be called anyway.
*/
if (AllTests.sqlGen instanceof MSSQLGenerator
|| AllTests.sqlGen instanceof OracleGenerator) {
return;
}
SQLGenerator sg = new DefaultSQLGenerator();
TableQuery query = new TableQuery("people", connectionPool);
SQLContainer container = new SQLContainer(query);
RowItem ri = (RowItem) container.getItem(container.getItemIds()
.iterator().next());
ri.getItemProperty("NAME").setValue("Viljami");
StatementHelper sh = sg.generateUpdateQuery("people", ri);
Assert.assertTrue("UPDATE people SET \"NAME\" = ?, \"AGE\" = ? WHERE \"ID\" = ?"
.equals(sh.getQueryString())
|| "UPDATE people SET \"AGE\" = ?, \"NAME\" = ? WHERE \"ID\" = ?"
.equals(sh.getQueryString()));
}
@Test
public void generateInsertQuery_basicQuery_shouldSucceed()
throws SQLException {
/*
* No need to run this for Oracle/MSSQL generators since the
* DefaultSQLGenerator method would be called anyway.
*/
if (AllTests.sqlGen instanceof MSSQLGenerator
|| AllTests.sqlGen instanceof OracleGenerator) {
return;
}
SQLGenerator sg = new DefaultSQLGenerator();
TableQuery query = new TableQuery("people", connectionPool);
SQLContainer container = new SQLContainer(query);
RowItem ri = (RowItem) container.getItem(container.addItem());
ri.getItemProperty("NAME").setValue("Viljami");
StatementHelper sh = sg.generateInsertQuery("people", ri);
Assert.assertTrue("INSERT INTO people (\"NAME\", \"AGE\") VALUES (?, ?)"
.equals(sh.getQueryString())
|| "INSERT INTO people (\"AGE\", \"NAME\") VALUES (?, ?)"
.equals(sh.getQueryString()));
}
@Test
public void generateComplexSelectQuery_forOracle_shouldSucceed()
throws SQLException {
SQLGenerator sg = new OracleGenerator();
List<Filter> f = new ArrayList<Filter>();
f.add(new Like("name", "%lle"));
List<OrderBy> ob = Arrays.asList(new OrderBy("name", true));
StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8,
"NAME, ID");
Assert.assertEquals(
"SELECT * FROM (SELECT x.*, ROWNUM AS \"rownum\" FROM"
+ " (SELECT NAME, ID FROM TABLE WHERE \"name\" LIKE ?"
+ " ORDER BY \"name\" ASC) x) WHERE \"rownum\" BETWEEN 5 AND 12",
sh.getQueryString());
}
@Test
public void generateComplexSelectQuery_forMSSQL_shouldSucceed()
throws SQLException {
SQLGenerator sg = new MSSQLGenerator();
List<Filter> f = new ArrayList<Filter>();
f.add(new Like("name", "%lle"));
List<OrderBy> ob = Arrays.asList(new OrderBy("name", true));
StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8,
"NAME, ID");
Assert.assertEquals(sh.getQueryString(),
"SELECT * FROM (SELECT row_number() OVER "
+ "( ORDER BY \"name\" ASC) AS rownum, NAME, ID "
+ "FROM TABLE WHERE \"name\" LIKE ?) "
+ "AS a WHERE a.rownum BETWEEN 5 AND 12");
}
@Test
public void generateComplexSelectQuery_forOracle_exclusiveFilteringMode_shouldSucceed()
throws SQLException {
SQLGenerator sg = new OracleGenerator();
List<Filter> f = new ArrayList<Filter>();
f.add(new Or(new Like("name", "%lle"), new Like("name", "vi%")));
List<OrderBy> ob = Arrays.asList(new OrderBy("name", true));
StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8,
"NAME, ID");
Assert.assertEquals(
sh.getQueryString(),
"SELECT * FROM (SELECT x.*, ROWNUM AS \"rownum\" FROM"
+ " (SELECT NAME, ID FROM TABLE WHERE (\"name\" LIKE ?"
+ " OR \"name\" LIKE ?) "
+ "ORDER BY \"name\" ASC) x) WHERE \"rownum\" BETWEEN 5 AND 12");
}
@Test
public void generateComplexSelectQuery_forMSSQL_exclusiveFilteringMode_shouldSucceed()
throws SQLException {
SQLGenerator sg = new MSSQLGenerator();
List<Filter> f = new ArrayList<Filter>();
f.add(new Or(new Like("name", "%lle"), new Like("name", "vi%")));
List<OrderBy> ob = Arrays.asList(new OrderBy("name", true));
StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8,
"NAME, ID");
Assert.assertEquals(sh.getQueryString(),
"SELECT * FROM (SELECT row_number() OVER "
+ "( ORDER BY \"name\" ASC) AS rownum, NAME, ID "
+ "FROM TABLE WHERE (\"name\" LIKE ? "
+ "OR \"name\" LIKE ?)) "
+ "AS a WHERE a.rownum BETWEEN 5 AND 12");
}
}

+ 897
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/query/FreeformQueryTest.java Visa fil

@@ -0,0 +1,897 @@
package com.vaadin.tests.server.container.sqlcontainer.query;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.RowId;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.SQLContainer;
import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
import com.vaadin.data.util.filter.Like;
import com.vaadin.data.util.query.FreeformQuery;
import com.vaadin.data.util.query.FreeformQueryDelegate;
import com.vaadin.data.util.query.OrderBy;
import com.vaadin.tests.server.container.sqlcontainer.AllTests;
import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
import com.vaadin.tests.server.container.sqlcontainer.DataGenerator;

public class FreeformQueryTest {

private static final int offset = AllTests.offset;
private JDBCConnectionPool connectionPool;

@Before
public void setUp() throws SQLException {

try {
connectionPool = new SimpleJDBCConnectionPool(AllTests.dbDriver,
AllTests.dbURL, AllTests.dbUser, AllTests.dbPwd, 2, 2);
} catch (SQLException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}

DataGenerator.addPeopleToDatabase(connectionPool);
}

@After
public void tearDown() {
if (connectionPool != null) {
connectionPool.destroy();
}
}

@Test
public void construction_legalParameters_shouldSucceed() {
FreeformQuery ffQuery = new FreeformQuery("SELECT * FROM foo",
Arrays.asList("ID"), connectionPool);
Assert.assertArrayEquals(new Object[] { "ID" }, ffQuery
.getPrimaryKeyColumns().toArray());

Assert.assertEquals("SELECT * FROM foo", ffQuery.getQueryString());
}

@Test(expected = IllegalArgumentException.class)
public void construction_emptyQueryString_shouldFail() {
new FreeformQuery("", Arrays.asList("ID"), connectionPool);
}

@Test
public void construction_nullPrimaryKeys_shouldSucceed() {
new FreeformQuery("SELECT * FROM foo", null, connectionPool);
}

@Test
public void construction_nullPrimaryKeys2_shouldSucceed() {
new FreeformQuery("SELECT * FROM foo", connectionPool);
}

@Test
public void construction_emptyPrimaryKeys_shouldSucceed() {
new FreeformQuery("SELECT * FROM foo", connectionPool);
}

@Test(expected = IllegalArgumentException.class)
public void construction_emptyStringsInPrimaryKeys_shouldFail() {
new FreeformQuery("SELECT * FROM foo", Arrays.asList(""),
connectionPool);
}

@Test(expected = IllegalArgumentException.class)
public void construction_nullConnectionPool_shouldFail() {
new FreeformQuery("SELECT * FROM foo", Arrays.asList("ID"), null);
}

@Test
public void getCount_simpleQuery_returnsFour() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
Assert.assertEquals(4, query.getCount());
}

@Test(expected = SQLException.class)
public void getCount_illegalQuery_shouldThrowSQLException()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM asdf",
Arrays.asList("ID"), connectionPool);
query.getResults(0, 50);
}

@Test
public void getCount_simpleQueryTwoMorePeopleAdded_returnsSix()
throws SQLException {
// Add some people
Connection conn = connectionPool.reserveConnection();
Statement statement = conn.createStatement();
if (AllTests.db == DB.MSSQL) {
statement.executeUpdate("insert into people values('Bengt', 30)");
statement.executeUpdate("insert into people values('Ingvar', 50)");
} else {
statement
.executeUpdate("insert into people values(default, 'Bengt', 30)");
statement
.executeUpdate("insert into people values(default, 'Ingvar', 50)");
}
statement.close();
conn.commit();
connectionPool.releaseConnection(conn);

FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);

Assert.assertEquals(6, query.getCount());
}

@Test
public void getCount_moreComplexQuery_returnsThree() throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT * FROM people WHERE \"NAME\" LIKE '%lle'",
connectionPool, new String[] { "ID" });
Assert.assertEquals(3, query.getCount());
}

@Test
public void getCount_normalState_releasesConnection() throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT * FROM people WHERE \"NAME\" LIKE '%lle'",
connectionPool, "ID");
query.getCount();
query.getCount();
Assert.assertNotNull(connectionPool.reserveConnection());
}

@Test
public void getCount_delegateRegistered_shouldUseDelegate()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(delegate.getCountQuery()).andReturn(
"SELECT COUNT(*) FROM people WHERE \"NAME\" LIKE '%lle'");
EasyMock.replay(delegate);
query.setDelegate(delegate);
Assert.assertEquals(3, query.getCount());
EasyMock.verify(delegate);
}

@Test
public void getCount_delegateRegisteredZeroRows_returnsZero()
throws SQLException {
DataGenerator.createGarbage(connectionPool);
FreeformQuery query = new FreeformQuery("SELECT * FROM GARBAGE",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(delegate.getCountQuery()).andReturn(
"SELECT COUNT(*) FROM GARBAGE");
EasyMock.replay(delegate);
query.setDelegate(delegate);
Assert.assertEquals(0, query.getCount());
EasyMock.verify(delegate);
}

@Test
public void getResults_simpleQuery_returnsFourRecords() throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT \"ID\",\"NAME\" FROM people", Arrays.asList("ID"),
connectionPool);
query.beginTransaction();
ResultSet rs = query.getResults(0, 0);

Assert.assertTrue(rs.next());
Assert.assertEquals(0 + offset, rs.getInt(1));
Assert.assertEquals("Ville", rs.getString(2));

Assert.assertTrue(rs.next());
Assert.assertEquals(1 + offset, rs.getInt(1));
Assert.assertEquals("Kalle", rs.getString(2));

Assert.assertTrue(rs.next());
Assert.assertEquals(2 + offset, rs.getInt(1));
Assert.assertEquals("Pelle", rs.getString(2));

Assert.assertTrue(rs.next());
Assert.assertEquals(3 + offset, rs.getInt(1));
Assert.assertEquals("Börje", rs.getString(2));

Assert.assertFalse(rs.next());
query.commit();
}

@Test
public void getResults_moreComplexQuery_returnsThreeRecords()
throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT * FROM people WHERE \"NAME\" LIKE '%lle'",
Arrays.asList("ID"), connectionPool);
query.beginTransaction();
ResultSet rs = query.getResults(0, 0);

Assert.assertTrue(rs.next());
Assert.assertEquals(0 + offset, rs.getInt(1));
Assert.assertEquals("Ville", rs.getString(2));

Assert.assertTrue(rs.next());
Assert.assertEquals(1 + offset, rs.getInt(1));
Assert.assertEquals("Kalle", rs.getString(2));

Assert.assertTrue(rs.next());
Assert.assertEquals(2 + offset, rs.getInt(1));
Assert.assertEquals("Pelle", rs.getString(2));

Assert.assertFalse(rs.next());
query.commit();
}

@Test
public void getResults_noDelegate5000Rows_returns5000rows()
throws SQLException {
DataGenerator.addFiveThousandPeople(connectionPool);

FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.beginTransaction();
ResultSet rs = query.getResults(0, 0);
for (int i = 0; i < 5000; i++) {
Assert.assertTrue(rs.next());
}
Assert.assertFalse(rs.next());
query.commit();
}

@Test(expected = UnsupportedOperationException.class)
public void setFilters_noDelegate_shouldFail() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Like("name", "%lle"));
query.setFilters(filters);
}

@Test(expected = UnsupportedOperationException.class)
public void setOrderBy_noDelegate_shouldFail() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.setOrderBy(Arrays.asList(new OrderBy("name", true)));
}

@Test(expected = IllegalStateException.class)
public void storeRow_noDelegateNoTransactionActive_shouldFail()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.storeRow(new RowItem(new SQLContainer(query), new RowId(
new Object[] { 1 }), null));
}

@Test(expected = UnsupportedOperationException.class)
public void storeRow_noDelegate_shouldFail() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
SQLContainer container = EasyMock.createNiceMock(SQLContainer.class);
EasyMock.replay(container);
query.beginTransaction();
query.storeRow(new RowItem(container, new RowId(new Object[] { 1 }),
null));
query.commit();
EasyMock.verify(container);
}

@Test(expected = UnsupportedOperationException.class)
public void removeRow_noDelegate_shouldFail() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
SQLContainer container = EasyMock.createNiceMock(SQLContainer.class);
EasyMock.replay(container);
query.beginTransaction();
query.removeRow(new RowItem(container, new RowId(new Object[] { 1 }),
null));
query.commit();
EasyMock.verify(container);
}

@Test
public void beginTransaction_readOnly_shouldSucceed() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.beginTransaction();
}

@Test
public void commit_readOnly_shouldSucceed() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.beginTransaction();
query.commit();
}

@Test
public void rollback_readOnly_shouldSucceed() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.beginTransaction();
query.rollback();
}

@Test(expected = SQLException.class)
public void commit_noActiveTransaction_shouldFail() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.commit();
}

@Test(expected = SQLException.class)
public void rollback_noActiveTransaction_shouldFail() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.rollback();
}

@Test
public void containsRowWithKeys_simpleQueryWithExistingKeys_returnsTrue()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
Assert.assertTrue(query.containsRowWithKey(1));
}

@Test
public void containsRowWithKeys_simpleQueryWithNonexistingKeys_returnsTrue()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
Assert.assertFalse(query.containsRowWithKey(1337));
}

// (expected = SQLException.class)
@Test
public void containsRowWithKeys_simpleQueryWithInvalidKeys_shouldFail()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
Assert.assertFalse(query.containsRowWithKey(38796));
}

@Test
public void containsRowWithKeys_queryContainingWhereClauseAndExistingKeys_returnsTrue()
throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT * FROM people WHERE \"NAME\" LIKE '%lle'",
Arrays.asList("ID"), connectionPool);
Assert.assertTrue(query.containsRowWithKey(1));
}

@Test
public void containsRowWithKeys_queryContainingLowercaseWhereClauseAndExistingKeys_returnsTrue()
throws SQLException {
FreeformQuery query = new FreeformQuery(
"select * from people where \"NAME\" like '%lle'",
Arrays.asList("ID"), connectionPool);
Assert.assertTrue(query.containsRowWithKey(1));
}

@Test
public void containsRowWithKeys_nullKeys_shouldFailAndReleaseConnections()
throws SQLException {
FreeformQuery query = new FreeformQuery(
"select * from people where \"NAME\" like '%lle'",
Arrays.asList("ID"), connectionPool);
try {
query.containsRowWithKey(new Object[] { null });
} catch (SQLException e) {
// We should now be able to reserve two connections
connectionPool.reserveConnection();
connectionPool.reserveConnection();
}
}

/*
* -------- Tests with a delegate ---------
*/

@Test
public void setDelegate_noExistingDelegate_shouldRegisterNewDelegate() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
query.setDelegate(delegate);
Assert.assertEquals(delegate, query.getDelegate());
}

@Test
public void getResults_hasDelegate_shouldCallDelegate() throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
if (AllTests.db == DB.MSSQL) {
EasyMock.expect(delegate.getQueryString(0, 2))
.andReturn(
"SELECT * FROM (SELECT row_number()"
+ "OVER (ORDER BY id ASC) AS rownum, * FROM people)"
+ " AS a WHERE a.rownum BETWEEN 0 AND 2");
} else if (AllTests.db == DB.ORACLE) {
EasyMock.expect(delegate.getQueryString(0, 2))
.andReturn(
"SELECT * FROM (SELECT x.*, ROWNUM AS r FROM"
+ " (SELECT * FROM people) x) WHERE r BETWEEN 1 AND 2");
} else {
EasyMock.expect(delegate.getQueryString(0, 2)).andReturn(
"SELECT * FROM people LIMIT 2 OFFSET 0");
}
EasyMock.replay(delegate);

query.setDelegate(delegate);
query.beginTransaction();
query.getResults(0, 2);
EasyMock.verify(delegate);
query.commit();
}

@Test
public void getResults_delegateImplementsGetQueryString_shouldHonorOffsetAndPagelength()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
if (AllTests.db == DB.MSSQL) {
EasyMock.expect(delegate.getQueryString(0, 2))
.andReturn(
"SELECT * FROM (SELECT row_number()"
+ "OVER (ORDER BY id ASC) AS rownum, * FROM people)"
+ " AS a WHERE a.rownum BETWEEN 0 AND 2");
} else if (AllTests.db == DB.ORACLE) {
EasyMock.expect(delegate.getQueryString(0, 2))
.andReturn(
"SELECT * FROM (SELECT x.*, ROWNUM AS r FROM"
+ " (SELECT * FROM people) x) WHERE r BETWEEN 1 AND 2");
} else {
EasyMock.expect(delegate.getQueryString(0, 2)).andReturn(
"SELECT * FROM people LIMIT 2 OFFSET 0");
}
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
ResultSet rs = query.getResults(0, 2);
int rsoffset = 0;
if (AllTests.db == DB.MSSQL) {
rsoffset++;
}
Assert.assertTrue(rs.next());
Assert.assertEquals(0 + offset, rs.getInt(1 + rsoffset));
Assert.assertEquals("Ville", rs.getString(2 + rsoffset));

Assert.assertTrue(rs.next());
Assert.assertEquals(1 + offset, rs.getInt(1 + rsoffset));
Assert.assertEquals("Kalle", rs.getString(2 + rsoffset));

Assert.assertFalse(rs.next());

EasyMock.verify(delegate);
query.commit();
}

@Test
public void getResults_delegateRegistered5000Rows_returns100rows()
throws SQLException {
DataGenerator.addFiveThousandPeople(connectionPool);
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
if (AllTests.db == DB.MSSQL) {
EasyMock.expect(delegate.getQueryString(200, 100))
.andReturn(
"SELECT * FROM (SELECT row_number()"
+ "OVER (ORDER BY id ASC) AS rownum, * FROM people)"
+ " AS a WHERE a.rownum BETWEEN 201 AND 300");
} else if (AllTests.db == DB.ORACLE) {
EasyMock.expect(delegate.getQueryString(200, 100))
.andReturn(
"SELECT * FROM (SELECT x.*, ROWNUM AS r FROM"
+ " (SELECT * FROM people ORDER BY ID ASC) x) WHERE r BETWEEN 201 AND 300");
} else {
EasyMock.expect(delegate.getQueryString(200, 100)).andReturn(
"SELECT * FROM people LIMIT 100 OFFSET 200");
}
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
ResultSet rs = query.getResults(200, 100);
for (int i = 0; i < 100; i++) {
Assert.assertTrue(rs.next());
Assert.assertEquals(200 + i + offset, rs.getInt("ID"));
}
Assert.assertFalse(rs.next());
query.commit();
}

@Test
public void setFilters_delegateImplementsSetFilters_shouldPassFiltersToDelegate() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
List<Filter> filters = new ArrayList<Filter>();
filters.add(new Like("name", "%lle"));
delegate.setFilters(filters);

EasyMock.replay(delegate);
query.setDelegate(delegate);

query.setFilters(filters);

EasyMock.verify(delegate);
}

@Test(expected = UnsupportedOperationException.class)
public void setFilters_delegateDoesNotImplementSetFilters_shouldFail() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
List<Filter> filters = new ArrayList<Filter>();
filters.add(new Like("name", "%lle"));
delegate.setFilters(filters);
EasyMock.expectLastCall().andThrow(new UnsupportedOperationException());
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.setFilters(filters);

EasyMock.verify(delegate);
}

@Test
public void setOrderBy_delegateImplementsSetOrderBy_shouldPassArgumentsToDelegate() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
List<OrderBy> orderBys = Arrays.asList(new OrderBy("name", false));
delegate.setOrderBy(orderBys);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.setOrderBy(orderBys);

EasyMock.verify(delegate);
}

@Test(expected = UnsupportedOperationException.class)
public void setOrderBy_delegateDoesNotImplementSetOrderBy_shouldFail() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
List<OrderBy> orderBys = Arrays.asList(new OrderBy("name", false));
delegate.setOrderBy(orderBys);
EasyMock.expectLastCall().andThrow(new UnsupportedOperationException());
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.setOrderBy(orderBys);

EasyMock.verify(delegate);
}

@Test
public void setFilters_noDelegateAndNullParameter_shouldSucceed() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.setFilters(null);
}

@Test
public void setOrderBy_noDelegateAndNullParameter_shouldSucceed() {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
query.setOrderBy(null);
}

@Test
public void storeRow_delegateImplementsStoreRow_shouldPassToDelegate()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(
delegate.storeRow(EasyMock.isA(Connection.class),
EasyMock.isA(RowItem.class))).andReturn(1);
SQLContainer container = EasyMock.createNiceMock(SQLContainer.class);
EasyMock.replay(delegate, container);
query.setDelegate(delegate);

query.beginTransaction();
RowItem row = new RowItem(container, new RowId(new Object[] { 1 }),
null);
query.storeRow(row);
query.commit();

EasyMock.verify(delegate, container);
}

@Test(expected = UnsupportedOperationException.class)
public void storeRow_delegateDoesNotImplementStoreRow_shouldFail()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(
delegate.storeRow(EasyMock.isA(Connection.class),
EasyMock.isA(RowItem.class))).andThrow(
new UnsupportedOperationException());
SQLContainer container = EasyMock.createNiceMock(SQLContainer.class);
EasyMock.replay(delegate, container);
query.setDelegate(delegate);

query.beginTransaction();
RowItem row = new RowItem(container, new RowId(new Object[] { 1 }),
null);
query.storeRow(row);
query.commit();

EasyMock.verify(delegate, container);
}

@Test
public void removeRow_delegateImplementsRemoveRow_shouldPassToDelegate()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(
delegate.removeRow(EasyMock.isA(Connection.class),
EasyMock.isA(RowItem.class))).andReturn(true);
SQLContainer container = EasyMock.createNiceMock(SQLContainer.class);
EasyMock.replay(delegate, container);
query.setDelegate(delegate);

query.beginTransaction();
RowItem row = new RowItem(container, new RowId(new Object[] { 1 }),
null);
query.removeRow(row);
query.commit();

EasyMock.verify(delegate, container);
}

@Test(expected = UnsupportedOperationException.class)
public void removeRow_delegateDoesNotImplementRemoveRow_shouldFail()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(
delegate.removeRow(EasyMock.isA(Connection.class),
EasyMock.isA(RowItem.class))).andThrow(
new UnsupportedOperationException());
SQLContainer container = EasyMock.createNiceMock(SQLContainer.class);
EasyMock.replay(delegate, container);
query.setDelegate(delegate);

query.beginTransaction();
RowItem row = new RowItem(container, new RowId(new Object[] { 1 }),
null);
query.removeRow(row);
query.commit();

EasyMock.verify(delegate, container);
}

@Test
public void beginTransaction_delegateRegistered_shouldSucceed()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
}

@Test(expected = IllegalStateException.class)
public void beginTransaction_transactionAlreadyActive_shouldFail()
throws SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);

query.beginTransaction();
query.beginTransaction();
}

@Test(expected = SQLException.class)
public void commit_delegateRegisteredNoActiveTransaction_shouldFail()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.commit();
}

@Test
public void commit_delegateRegisteredActiveTransaction_shouldSucceed()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
query.commit();
}

@Test(expected = SQLException.class)
public void commit_delegateRegisteredActiveTransactionDoubleCommit_shouldFail()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
query.commit();
query.commit();
}

@Test(expected = SQLException.class)
public void rollback_delegateRegisteredNoActiveTransaction_shouldFail()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.rollback();
}

@Test
public void rollback_delegateRegisteredActiveTransaction_shouldSucceed()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
query.rollback();
}

@Test(expected = SQLException.class)
public void rollback_delegateRegisteredActiveTransactionDoubleRollback_shouldFail()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
query.rollback();
query.rollback();
}

@Test(expected = SQLException.class)
public void rollback_delegateRegisteredCommittedTransaction_shouldFail()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
query.commit();
query.rollback();
}

@Test(expected = SQLException.class)
public void commit_delegateRegisteredRollbackedTransaction_shouldFail()
throws UnsupportedOperationException, SQLException {
FreeformQuery query = new FreeformQuery("SELECT * FROM people",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.beginTransaction();
query.rollback();
query.commit();
}

@Test(expected = SQLException.class)
public void containsRowWithKeys_delegateRegistered_shouldCallGetContainsRowQueryString()
throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT * FROM people WHERE name LIKE '%lle'",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(delegate.getContainsRowQueryString(1)).andReturn("");
EasyMock.replay(delegate);
query.setDelegate(delegate);

query.containsRowWithKey(1);

EasyMock.verify(delegate);
}

@Test
public void containsRowWithKeys_delegateRegistered_shouldUseResultFromGetContainsRowQueryString()
throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT * FROM people WHERE \"NAME\" LIKE '%lle'",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
// In order to test that this is the query that is actually used, we use
// a non-existing id in place of the existing one.
EasyMock.expect(delegate.getContainsRowQueryString(1))
.andReturn(
"SELECT * FROM people WHERE \"NAME\" LIKE '%lle' AND \"ID\" = 1337");
EasyMock.replay(delegate);
query.setDelegate(delegate);

// The id (key) used should be 1337 as above, for the call with key = 1
Assert.assertFalse(query.containsRowWithKey(1));

EasyMock.verify(delegate);
}

@Test
public void containsRowWithKeys_delegateRegisteredGetContainsRowQueryStringNotImplemented_shouldBuildQueryString()
throws SQLException {
FreeformQuery query = new FreeformQuery(
"SELECT * FROM people WHERE \"NAME\" LIKE '%lle'",
Arrays.asList("ID"), connectionPool);
FreeformQueryDelegate delegate = EasyMock
.createMock(FreeformQueryDelegate.class);
EasyMock.expect(delegate.getContainsRowQueryString(1)).andThrow(
new UnsupportedOperationException());
EasyMock.replay(delegate);
query.setDelegate(delegate);

Assert.assertTrue(query.containsRowWithKey(1));

EasyMock.verify(delegate);
}
}

+ 311
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/query/QueryBuilderTest.java Visa fil

@@ -0,0 +1,311 @@
package com.vaadin.tests.server.container.sqlcontainer.query;

import java.util.ArrayList;

import junit.framework.Assert;

import org.easymock.EasyMock;
import org.junit.Test;

import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.And;
import com.vaadin.data.util.filter.Between;
import com.vaadin.data.util.filter.Compare.Equal;
import com.vaadin.data.util.filter.Compare.Greater;
import com.vaadin.data.util.filter.Compare.GreaterOrEqual;
import com.vaadin.data.util.filter.Compare.Less;
import com.vaadin.data.util.filter.Compare.LessOrEqual;
import com.vaadin.data.util.filter.IsNull;
import com.vaadin.data.util.filter.Like;
import com.vaadin.data.util.filter.Not;
import com.vaadin.data.util.filter.Or;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.data.util.query.generator.StatementHelper;
import com.vaadin.data.util.query.generator.filter.QueryBuilder;
import com.vaadin.data.util.query.generator.filter.StringDecorator;

public class QueryBuilderTest {

private StatementHelper mockedStatementHelper(Object... values) {
StatementHelper sh = EasyMock.createMock(StatementHelper.class);
for (Object val : values) {
sh.addParameterValue(val);
EasyMock.expectLastCall();
}
EasyMock.replay(sh);
return sh;
}

// escape bad characters and wildcards

@Test
public void getWhereStringForFilter_equals() {
StatementHelper sh = mockedStatementHelper("Fido");
Equal f = new Equal("NAME", "Fido");
Assert.assertEquals("\"NAME\" = ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_greater() {
StatementHelper sh = mockedStatementHelper(18);
Greater f = new Greater("AGE", 18);
Assert.assertEquals("\"AGE\" > ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_less() {
StatementHelper sh = mockedStatementHelper(65);
Less f = new Less("AGE", 65);
Assert.assertEquals("\"AGE\" < ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_greaterOrEqual() {
StatementHelper sh = mockedStatementHelper(18);
GreaterOrEqual f = new GreaterOrEqual("AGE", 18);
Assert.assertEquals("\"AGE\" >= ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_lessOrEqual() {
StatementHelper sh = mockedStatementHelper(65);
LessOrEqual f = new LessOrEqual("AGE", 65);
Assert.assertEquals("\"AGE\" <= ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_simpleStringFilter() {
StatementHelper sh = mockedStatementHelper("Vi%");
SimpleStringFilter f = new SimpleStringFilter("NAME", "Vi", false, true);
Assert.assertEquals("\"NAME\" LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_simpleStringFilterMatchAnywhere() {
StatementHelper sh = mockedStatementHelper("%Vi%");
SimpleStringFilter f = new SimpleStringFilter("NAME", "Vi", false,
false);
Assert.assertEquals("\"NAME\" LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_simpleStringFilterMatchAnywhereIgnoreCase() {
StatementHelper sh = mockedStatementHelper("%VI%");
SimpleStringFilter f = new SimpleStringFilter("NAME", "Vi", true, false);
Assert.assertEquals("UPPER(\"NAME\") LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_startsWith() {
StatementHelper sh = mockedStatementHelper("Vi%");
Like f = new Like("NAME", "Vi%");
Assert.assertEquals("\"NAME\" LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_startsWithNumber() {
StatementHelper sh = mockedStatementHelper("1%");
Like f = new Like("AGE", "1%");
Assert.assertEquals("\"AGE\" LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_endsWith() {
StatementHelper sh = mockedStatementHelper("%lle");
Like f = new Like("NAME", "%lle");
Assert.assertEquals("\"NAME\" LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_contains() {
StatementHelper sh = mockedStatementHelper("%ill%");
Like f = new Like("NAME", "%ill%");
Assert.assertEquals("\"NAME\" LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_between() {
StatementHelper sh = mockedStatementHelper(18, 65);
Between f = new Between("AGE", 18, 65);
Assert.assertEquals("\"AGE\" BETWEEN ? AND ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_caseInsensitive_equals() {
StatementHelper sh = mockedStatementHelper("FIDO");
Like f = new Like("NAME", "Fido");
f.setCaseSensitive(false);
Assert.assertEquals("UPPER(\"NAME\") LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_caseInsensitive_startsWith() {
StatementHelper sh = mockedStatementHelper("VI%");
Like f = new Like("NAME", "Vi%");
f.setCaseSensitive(false);
Assert.assertEquals("UPPER(\"NAME\") LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_caseInsensitive_endsWith() {
StatementHelper sh = mockedStatementHelper("%LLE");
Like f = new Like("NAME", "%lle");
f.setCaseSensitive(false);
Assert.assertEquals("UPPER(\"NAME\") LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilter_caseInsensitive_contains() {
StatementHelper sh = mockedStatementHelper("%ILL%");
Like f = new Like("NAME", "%ill%");
f.setCaseSensitive(false);
Assert.assertEquals("UPPER(\"NAME\") LIKE ?",
QueryBuilder.getWhereStringForFilter(f, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_listOfFilters() {
StatementHelper sh = mockedStatementHelper("%lle", 18);
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Like("NAME", "%lle"));
filters.add(new Greater("AGE", 18));
Assert.assertEquals(" WHERE \"NAME\" LIKE ? AND \"AGE\" > ?",
QueryBuilder.getWhereStringForFilters(filters, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_oneAndFilter() {
StatementHelper sh = mockedStatementHelper("%lle", 18);
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new And(new Like("NAME", "%lle"), new Greater("AGE", 18)));
Assert.assertEquals(" WHERE (\"NAME\" LIKE ? AND \"AGE\" > ?)",
QueryBuilder.getWhereStringForFilters(filters, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_oneOrFilter() {
StatementHelper sh = mockedStatementHelper("%lle", 18);
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Or(new Like("NAME", "%lle"), new Greater("AGE", 18)));
Assert.assertEquals(" WHERE (\"NAME\" LIKE ? OR \"AGE\" > ?)",
QueryBuilder.getWhereStringForFilters(filters, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_complexCompoundFilters() {
StatementHelper sh = mockedStatementHelper("%lle", 18, 65, "Pelle");
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Or(new And(new Like("NAME", "%lle"), new Or(new Less(
"AGE", 18), new Greater("AGE", 65))),
new Equal("NAME", "Pelle")));
Assert.assertEquals(
" WHERE ((\"NAME\" LIKE ? AND (\"AGE\" < ? OR \"AGE\" > ?)) OR \"NAME\" = ?)",
QueryBuilder.getWhereStringForFilters(filters, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_complexCompoundFiltersAndSingleFilter() {
StatementHelper sh = mockedStatementHelper("%lle", 18, 65, "Pelle",
"Virtanen");
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Or(new And(new Like("NAME", "%lle"), new Or(new Less(
"AGE", 18), new Greater("AGE", 65))),
new Equal("NAME", "Pelle")));
filters.add(new Equal("LASTNAME", "Virtanen"));
Assert.assertEquals(
" WHERE ((\"NAME\" LIKE ? AND (\"AGE\" < ? OR \"AGE\" > ?)) OR \"NAME\" = ?) AND \"LASTNAME\" = ?",
QueryBuilder.getWhereStringForFilters(filters, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_emptyList_shouldReturnEmptyString() {
ArrayList<Filter> filters = new ArrayList<Filter>();
Assert.assertEquals("", QueryBuilder.getWhereStringForFilters(filters,
new StatementHelper()));
}

@Test
public void getWhereStringForFilters_NotFilter() {
StatementHelper sh = mockedStatementHelper(18);
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Not(new Equal("AGE", 18)));
Assert.assertEquals(" WHERE NOT \"AGE\" = ?",
QueryBuilder.getWhereStringForFilters(filters, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_complexNegatedFilter() {
StatementHelper sh = mockedStatementHelper(65, 18);
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Not(new Or(new Equal("AGE", 65), new Equal("AGE", 18))));
Assert.assertEquals(" WHERE NOT (\"AGE\" = ? OR \"AGE\" = ?)",
QueryBuilder.getWhereStringForFilters(filters, sh));
EasyMock.verify(sh);
}

@Test
public void getWhereStringForFilters_isNull() {
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new IsNull("NAME"));
Assert.assertEquals(" WHERE \"NAME\" IS NULL", QueryBuilder
.getWhereStringForFilters(filters, new StatementHelper()));
}

@Test
public void getWhereStringForFilters_isNotNull() {
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Not(new IsNull("NAME")));
Assert.assertEquals(" WHERE \"NAME\" IS NOT NULL", QueryBuilder
.getWhereStringForFilters(filters, new StatementHelper()));
}

@Test
public void getWhereStringForFilters_customStringDecorator() {
QueryBuilder.setStringDecorator(new StringDecorator("[", "]"));
ArrayList<Filter> filters = new ArrayList<Filter>();
filters.add(new Not(new IsNull("NAME")));
Assert.assertEquals(" WHERE [NAME] IS NOT NULL", QueryBuilder
.getWhereStringForFilters(filters, new StatementHelper()));
// Reset the default string decorator
QueryBuilder.setStringDecorator(new StringDecorator("\"", "\""));
}
}

+ 619
- 0
tests/src/com/vaadin/tests/server/container/sqlcontainer/query/TableQueryTest.java Visa fil

@@ -0,0 +1,619 @@
package com.vaadin.tests.server.container.sqlcontainer.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.OptimisticLockException;
import com.vaadin.data.util.RowItem;
import com.vaadin.data.util.SQLContainer;
import com.vaadin.data.util.connection.JDBCConnectionPool;
import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
import com.vaadin.data.util.filter.Compare.Equal;
import com.vaadin.data.util.filter.Like;
import com.vaadin.data.util.query.OrderBy;
import com.vaadin.data.util.query.TableQuery;
import com.vaadin.data.util.query.generator.DefaultSQLGenerator;
import com.vaadin.tests.server.container.sqlcontainer.AllTests;
import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
import com.vaadin.tests.server.container.sqlcontainer.DataGenerator;
public class TableQueryTest {
private static final int offset = AllTests.offset;
private JDBCConnectionPool connectionPool;
@Before
public void setUp() throws SQLException {
try {
connectionPool = new SimpleJDBCConnectionPool(AllTests.dbDriver,
AllTests.dbURL, AllTests.dbUser, AllTests.dbPwd, 2, 2);
} catch (SQLException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
DataGenerator.addPeopleToDatabase(connectionPool);
}
@After
public void tearDown() {
if (connectionPool != null) {
connectionPool.destroy();
}
}
/**********************************************************************
* TableQuery construction tests
**********************************************************************/
@Test
public void construction_legalParameters_shouldSucceed() {
TableQuery tQuery = new TableQuery("people", connectionPool,
new DefaultSQLGenerator());
Assert.assertArrayEquals(new Object[] { "ID" }, tQuery
.getPrimaryKeyColumns().toArray());
boolean correctTableName = "people".equalsIgnoreCase(tQuery
.getTableName());
Assert.assertTrue(correctTableName);
}
@Test
public void construction_legalParameters_defaultGenerator_shouldSucceed() {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
Assert.assertArrayEquals(new Object[] { "ID" }, tQuery
.getPrimaryKeyColumns().toArray());
boolean correctTableName = "people".equalsIgnoreCase(tQuery
.getTableName());
Assert.assertTrue(correctTableName);
}
@Test(expected = IllegalArgumentException.class)
public void construction_nonExistingTableName_shouldFail() {
new TableQuery("skgwaguhsd", connectionPool, new DefaultSQLGenerator());
}
@Test(expected = IllegalArgumentException.class)
public void construction_emptyTableName_shouldFail() {
new TableQuery("", connectionPool, new DefaultSQLGenerator());
}
@Test(expected = IllegalArgumentException.class)
public void construction_nullSqlGenerator_shouldFail() {
new TableQuery("people", connectionPool, null);
}
@Test(expected = IllegalArgumentException.class)
public void construction_nullConnectionPool_shouldFail() {
new TableQuery("people", null, new DefaultSQLGenerator());
}
/**********************************************************************
* TableQuery row count tests
**********************************************************************/
@Test
public void getCount_simpleQuery_returnsFour() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
Assert.assertEquals(4, tQuery.getCount());
}
@Test
public void getCount_simpleQueryTwoMorePeopleAdded_returnsSix()
throws SQLException {
// Add some people
Connection conn = connectionPool.reserveConnection();
Statement statement = conn.createStatement();
if (AllTests.db == DB.MSSQL) {
statement.executeUpdate("insert into people values('Bengt', 30)");
statement.executeUpdate("insert into people values('Ingvar', 50)");
} else {
statement
.executeUpdate("insert into people values(default, 'Bengt', 30)");
statement
.executeUpdate("insert into people values(default, 'Ingvar', 50)");
}
statement.close();
conn.commit();
connectionPool.releaseConnection(conn);
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
Assert.assertEquals(6, tQuery.getCount());
}
@Test
public void getCount_normalState_releasesConnection() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.getCount();
tQuery.getCount();
Assert.assertNotNull(connectionPool.reserveConnection());
}
/**********************************************************************
* TableQuery get results tests
**********************************************************************/
@Test
public void getResults_simpleQuery_returnsFourRecords() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.beginTransaction();
ResultSet rs = tQuery.getResults(0, 0);
Assert.assertTrue(rs.next());
Assert.assertEquals(0 + offset, rs.getInt(1));
Assert.assertEquals("Ville", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(1 + offset, rs.getInt(1));
Assert.assertEquals("Kalle", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(2 + offset, rs.getInt(1));
Assert.assertEquals("Pelle", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(3 + offset, rs.getInt(1));
Assert.assertEquals("Börje", rs.getString(2));
Assert.assertFalse(rs.next());
tQuery.commit();
}
@Test
public void getResults_noDelegate5000Rows_returns5000rows()
throws SQLException {
DataGenerator.addFiveThousandPeople(connectionPool);
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.beginTransaction();
ResultSet rs = tQuery.getResults(0, 0);
for (int i = 0; i < 5000; i++) {
Assert.assertTrue(rs.next());
}
Assert.assertFalse(rs.next());
tQuery.commit();
}
/**********************************************************************
* TableQuery transaction management tests
**********************************************************************/
@Test
public void beginTransaction_readOnly_shouldSucceed() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.beginTransaction();
}
@Test(expected = IllegalStateException.class)
public void beginTransaction_transactionAlreadyActive_shouldFail()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.beginTransaction();
tQuery.beginTransaction();
}
@Test
public void commit_readOnly_shouldSucceed() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.beginTransaction();
tQuery.commit();
}
@Test
public void rollback_readOnly_shouldSucceed() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.beginTransaction();
tQuery.rollback();
}
@Test(expected = SQLException.class)
public void commit_noActiveTransaction_shouldFail() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.commit();
}
@Test(expected = SQLException.class)
public void rollback_noActiveTransaction_shouldFail() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.rollback();
}
/**********************************************************************
* TableQuery row query with given keys tests
**********************************************************************/
@Test
public void containsRowWithKeys_existingKeys_returnsTrue()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
Assert.assertTrue(tQuery.containsRowWithKey(1));
}
@Test
public void containsRowWithKeys_nonexistingKeys_returnsTrue()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
Assert.assertFalse(tQuery.containsRowWithKey(1337));
}
@Test
public void containsRowWithKeys_invalidKeys_shouldFail()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
boolean b = true;
try {
b = tQuery.containsRowWithKey("foo");
} catch (SQLException se) {
return;
}
Assert.assertFalse(b);
}
@Test
public void containsRowWithKeys_nullKeys_shouldFailAndReleaseConnections()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
try {
tQuery.containsRowWithKey(new Object[] { null });
} catch (SQLException e) {
// We should now be able to reserve two connections
connectionPool.reserveConnection();
connectionPool.reserveConnection();
}
}
/**********************************************************************
* TableQuery filtering and ordering tests
**********************************************************************/
@Test
public void setFilters_shouldReturnCorrectCount() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
List<Filter> filters = new ArrayList<Filter>();
filters.add(new Like("NAME", "%lle"));
tQuery.setFilters(filters);
Assert.assertEquals(3, tQuery.getCount());
}
@Test
public void setOrderByNameAscending_shouldReturnCorrectOrder()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
List<OrderBy> orderBys = Arrays.asList(new OrderBy("NAME", true));
tQuery.setOrderBy(orderBys);
tQuery.beginTransaction();
ResultSet rs;
rs = tQuery.getResults(0, 0);
Assert.assertTrue(rs.next());
Assert.assertEquals(3 + offset, rs.getInt(1));
Assert.assertEquals("Börje", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(1 + offset, rs.getInt(1));
Assert.assertEquals("Kalle", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(2 + offset, rs.getInt(1));
Assert.assertEquals("Pelle", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(0 + offset, rs.getInt(1));
Assert.assertEquals("Ville", rs.getString(2));
Assert.assertFalse(rs.next());
tQuery.commit();
}
@Test
public void setOrderByNameDescending_shouldReturnCorrectOrder()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
List<OrderBy> orderBys = Arrays.asList(new OrderBy("NAME", false));
tQuery.setOrderBy(orderBys);
tQuery.beginTransaction();
ResultSet rs;
rs = tQuery.getResults(0, 0);
Assert.assertTrue(rs.next());
Assert.assertEquals(0 + offset, rs.getInt(1));
Assert.assertEquals("Ville", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(2 + offset, rs.getInt(1));
Assert.assertEquals("Pelle", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(1 + offset, rs.getInt(1));
Assert.assertEquals("Kalle", rs.getString(2));
Assert.assertTrue(rs.next());
Assert.assertEquals(3 + offset, rs.getInt(1));
Assert.assertEquals("Börje", rs.getString(2));
Assert.assertFalse(rs.next());
tQuery.commit();
}
@Test
public void setFilters_nullParameter_shouldSucceed() {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.setFilters(null);
}
@Test
public void setOrderBy_nullParameter_shouldSucceed() {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.setOrderBy(null);
}
/**********************************************************************
* TableQuery row removal tests
**********************************************************************/
@Test
public void removeRowThroughContainer_legalRowItem_shouldSucceed()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
SQLContainer container = new SQLContainer(tQuery);
container.setAutoCommit(false);
Assert.assertTrue(container.removeItem(container.getItemIds()
.iterator().next()));
Assert.assertEquals(4, tQuery.getCount());
Assert.assertEquals(3, container.size());
container.commit();
Assert.assertEquals(3, tQuery.getCount());
Assert.assertEquals(3, container.size());
}
@Test
public void removeRowThroughContainer_nonexistingRowId_shouldFail()
throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
SQLContainer container = new SQLContainer(tQuery);
container.setAutoCommit(true);
Assert.assertFalse(container.removeItem("foo"));
}
/**********************************************************************
* TableQuery row adding / modification tests
**********************************************************************/
@Test
public void insertRowThroughContainer_shouldSucceed() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
tQuery.setVersionColumn("ID");
SQLContainer container = new SQLContainer(tQuery);
container.setAutoCommit(false);
Object item = container.addItem();
Assert.assertNotNull(item);
Assert.assertEquals(4, tQuery.getCount());
Assert.assertEquals(5, container.size());
container.commit();
Assert.assertEquals(5, tQuery.getCount());
Assert.assertEquals(5, container.size());
}
@Test
public void modifyRowThroughContainer_shouldSucceed() throws SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
// In this test the primary key is used as a version column
tQuery.setVersionColumn("ID");
SQLContainer container = new SQLContainer(tQuery);
container.setAutoCommit(false);
/* Check that the container size is correct and there is no 'Viljami' */
Assert.assertEquals(4, container.size());
List<Filter> filters = new ArrayList<Filter>();
filters.add(new Equal("NAME", "Viljami"));
tQuery.setFilters(filters);
Assert.assertEquals(0, tQuery.getCount());
tQuery.setFilters(null);
/* Fetch first item, modify and commit */
Object item = container.getItem(container.getItemIds().iterator()
.next());
Assert.assertNotNull(item);
RowItem ri = (RowItem) item;
Assert.assertNotNull(ri.getItemProperty("NAME"));
ri.getItemProperty("NAME").setValue("Viljami");
container.commit();
// Check that the size is still correct and only 1 'Viljami' is found
Assert.assertEquals(4, tQuery.getCount());
Assert.assertEquals(4, container.size());
tQuery.setFilters(filters);
Assert.assertEquals(1, tQuery.getCount());
}
@Test
public void storeRow_noVersionColumn_shouldSucceed()
throws UnsupportedOperationException, SQLException {
TableQuery tQuery = new TableQuery("people", connectionPool,
AllTests.sqlGen);
SQLContainer container = new SQLContainer(tQuery);
Object id = container.addItem();
RowItem row = (RowItem) container.getItem(id);
row.getItemProperty("NAME").setValue("R2D2");
row.getItemProperty("AGE").setValue(123);
tQuery.beginTransaction();
tQuery.storeRow(row);
tQuery.commit();
Connection conn = connectionPool.reserveConnection();
PreparedStatement stmt = conn
.prepareStatement("SELECT * FROM PEOPLE WHERE \"NAME\" = ?");
stmt.setString(1, "R2D2");
ResultSet rs = stmt.executeQuery();
Assert.assertTrue(rs.next());
rs.close();
stmt.close();
connectionPool.releaseConnection(conn);
}
@Test
public void storeRow_versionSetAndEqualToDBValue_shouldSucceed()
throws SQLException {
DataGenerator.addVersionedData(connectionPool);
TableQuery tQuery = new TableQuery("versioned", connectionPool,
AllTests.sqlGen);
tQuery.setVersionColumn("VERSION");
SQLContainer container = new SQLContainer(tQuery);
RowItem row = (RowItem) container.getItem(container.firstItemId());
Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue());
row.getItemProperty("TEXT").setValue("asdf");
container.commit();
Connection conn = connectionPool.reserveConnection();
PreparedStatement stmt = conn
.prepareStatement("SELECT * FROM VERSIONED WHERE \"TEXT\" = ?");
stmt.setString(1, "asdf");
ResultSet rs = stmt.executeQuery();
Assert.assertTrue(rs.next());
rs.close();
stmt.close();
conn.commit();
connectionPool.releaseConnection(conn);
}
@Test(expected = OptimisticLockException.class)
public void storeRow_versionSetAndLessThanDBValue_shouldThrowException()
throws SQLException {
if (AllTests.db == DB.HSQLDB) {
throw new OptimisticLockException(
"HSQLDB doesn't support row versioning for optimistic locking - don't run this test.",
null);
}
DataGenerator.addVersionedData(connectionPool);
TableQuery tQuery = new TableQuery("versioned", connectionPool,
AllTests.sqlGen);
tQuery.setVersionColumn("VERSION");
SQLContainer container = new SQLContainer(tQuery);
RowItem row = (RowItem) container.getItem(container.firstItemId());
Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue());
row.getItemProperty("TEXT").setValue("asdf");
// Update the version using another connection.
Connection conn = connectionPool.reserveConnection();
PreparedStatement stmt = conn
.prepareStatement("UPDATE VERSIONED SET \"TEXT\" = ? WHERE \"ID\" = ?");
stmt.setString(1, "foo");
stmt.setObject(2, row.getItemProperty("ID").getValue());
stmt.executeUpdate();
stmt.close();
conn.commit();
connectionPool.releaseConnection(conn);
container.commit();
}
@Test
public void removeRow_versionSetAndEqualToDBValue_shouldSucceed()
throws SQLException {
DataGenerator.addVersionedData(connectionPool);
TableQuery tQuery = new TableQuery("versioned", connectionPool,
AllTests.sqlGen);
tQuery.setVersionColumn("VERSION");
SQLContainer container = new SQLContainer(tQuery);
RowItem row = (RowItem) container.getItem(container.firstItemId());
Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue());
container.removeItem(container.firstItemId());
container.commit();
Connection conn = connectionPool.reserveConnection();
PreparedStatement stmt = conn
.prepareStatement("SELECT * FROM VERSIONED WHERE \"TEXT\" = ?");
stmt.setString(1, "Junk");
ResultSet rs = stmt.executeQuery();
Assert.assertFalse(rs.next());
rs.close();
stmt.close();
conn.commit();
connectionPool.releaseConnection(conn);
}
@Test(expected = OptimisticLockException.class)
public void removeRow_versionSetAndLessThanDBValue_shouldThrowException()
throws SQLException {
if (AllTests.db == AllTests.DB.HSQLDB) {
// HSQLDB doesn't support versioning, so this is to make the test
// green.
throw new OptimisticLockException(null);
}
DataGenerator.addVersionedData(connectionPool);
TableQuery tQuery = new TableQuery("versioned", connectionPool,
AllTests.sqlGen);
tQuery.setVersionColumn("VERSION");
SQLContainer container = new SQLContainer(tQuery);
RowItem row = (RowItem) container.getItem(container.firstItemId());
Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue());
// Update the version using another connection.
Connection conn = connectionPool.reserveConnection();
PreparedStatement stmt = conn
.prepareStatement("UPDATE VERSIONED SET \"TEXT\" = ? WHERE \"ID\" = ?");
stmt.setString(1, "asdf");
stmt.setObject(2, row.getItemProperty("ID").getValue());
stmt.executeUpdate();
stmt.close();
conn.commit();
connectionPool.releaseConnection(conn);
container.removeItem(container.firstItemId());
container.commit();
}
}

Laddar…
Avbryt
Spara