Merge branch 'stable-1.7'
* stable-1.7:
gwtorm 1.7.1
Add transaction support for Jdbc dialects
Fix a bug where "LIMIT ?" in queries was omitted in the generated query
Drop "bar" tables in test setup
Start 1.7.1 development
All the changes on stable-1.7 already exist on master.
Merged with `-s ours`.
Change-Id: I28c4a3f5757b85f0bb17ec2089e7682c1bbdb1bb
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 5002612..6dbcac5 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -1,15 +1,15 @@
#Wed May 13 16:33:08 PDT 2009
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
diff --git a/README_MAXDB b/README_MAXDB
new file mode 100644
index 0000000..7230e5b
--- /dev/null
+++ b/README_MAXDB
@@ -0,0 +1,23 @@
+To test DialectMaxDB a SAP MaxDB JDBC driver "sapdbc.jar" is needed. It is
+not available in a public maven repository. However, the driver can be found
+in your MaxDB installation at the following location:
+
+- on Windows 64bit at "C:\Program Files\sdb\MaxDB\runtime\jar\sapdbc.jar"
+- on Linux at "/opt/sdb/MaxDB/runtime/jar/sapdbc.jar"
+
+To execute tests on MaxDB, you firstly need to create a test user with an
+associated empty schema in your database. Then you can execute the tests
+using maven with the profile "maxdb". The following properties need to be set:
+
+maxdb.driver.jar=<path to maxdb jdbc driver>
+maxdb.url=<url of test database>
+maxdb.user=<user name>
+maxdb.password=<password of test user>
+
+So the complete command would be:
+
+mvn package -P maxdb
+ -Dmaxdb.driver.jar=<path to maxdb jdbc driver>
+ -Dmaxdb.url=<url of test database>
+ -Dmaxdb.user=<user name>
+ -Dmaxdb.password=<password of test user>
diff --git a/pom.xml b/pom.xml
index ef262f7..d5b3bd8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,11 +21,16 @@
<groupId>gwtorm</groupId>
<artifactId>gwtorm</artifactId>
<packaging>jar</packaging>
- <version>1.7.1</version>
+ <version>1.12-SNAPSHOT</version>
<name>gwtorm</name>
<description>Tiny ORM</description>
<url>https://gerrit.googlesource.com/gwtorm</url>
+ <scm>
+ <url>https://gerrit.googlesource.com/gwtorm</url>
+ <connection>https://gerrit.googlesource.com/gwtorm</connection>
+ </scm>
+
<mailingLists>
<mailingList>
<name>repo-discuss mailing list</name>
@@ -265,6 +270,23 @@
</dependencies>
</profile>
<profile>
+ <id>maxdb</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.17</version>
+ <configuration>
+ <additionalClasspathElements>
+ <additionalClasspathElement>${maxdb.driver.jar}</additionalClasspathElement>
+ </additionalClasspathElements>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
<id>skip-proprietary-databases</id>
<activation>
<activeByDefault>true</activeByDefault>
@@ -290,8 +312,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
- <source>1.6</source>
- <target>1.6</target>
+ <source>1.7</source>
+ <target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
@@ -305,6 +327,26 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9.1</version>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ <quiet>true</quiet>
+ <links>
+ <link>http://java.sun.com/j2se/1.7.0/docs/api</link>
+ </links>
+ </configuration>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
<extensions>
<extension>
@@ -319,14 +361,14 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
- <version>4.0</version>
+ <version>4.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>13.0.1</version>
+ <version>15.0</version>
<scope>compile</scope>
</dependency>
@@ -375,7 +417,7 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
- <version>2.4.1</version>
+ <version>2.5.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
diff --git a/src/main/java/com/google/gwtorm/client/CompoundKey.java b/src/main/java/com/google/gwtorm/client/CompoundKey.java
index 8224cac..0087ccb 100644
--- a/src/main/java/com/google/gwtorm/client/CompoundKey.java
+++ b/src/main/java/com/google/gwtorm/client/CompoundKey.java
@@ -33,6 +33,7 @@
/**
* @return the parent key instance; null if this is a root level key.
*/
+ @Override
public P getParentKey() {
return null;
}
@@ -92,6 +93,7 @@
return r.toString();
}
+ @Override
public void fromString(final String in) {
final String[] parts = in.split(",");
int p = 0;
diff --git a/src/main/java/com/google/gwtorm/client/IntKey.java b/src/main/java/com/google/gwtorm/client/IntKey.java
index e7995a3..f64493b 100644
--- a/src/main/java/com/google/gwtorm/client/IntKey.java
+++ b/src/main/java/com/google/gwtorm/client/IntKey.java
@@ -37,6 +37,7 @@
/**
* @return the parent key instance; null if this is a root level key.
*/
+ @Override
public P getParentKey() {
return null;
}
@@ -72,6 +73,7 @@
return r.toString();
}
+ @Override
public void fromString(final String in) {
set(Integer.parseInt(KeyUtil.parseFromString(getParentKey(), in)));
}
diff --git a/src/main/java/com/google/gwtorm/client/Key.java b/src/main/java/com/google/gwtorm/client/Key.java
index a08929a..b5b3d3a 100644
--- a/src/main/java/com/google/gwtorm/client/Key.java
+++ b/src/main/java/com/google/gwtorm/client/Key.java
@@ -31,11 +31,14 @@
*/
public P getParentKey();
+ @Override
public int hashCode();
+ @Override
public boolean equals(Object o);
/** @return the key, encoded in a string format . */
+ @Override
public String toString();
/** Reset this key instance to represent the data in the supplied string. */
diff --git a/src/main/java/com/google/gwtorm/client/KeyUtil.java b/src/main/java/com/google/gwtorm/client/KeyUtil.java
index d9caeb5..c4b4551 100644
--- a/src/main/java/com/google/gwtorm/client/KeyUtil.java
+++ b/src/main/java/com/google/gwtorm/client/KeyUtil.java
@@ -90,6 +90,7 @@
if (comma < 0 && parent != null) {
throw new IllegalArgumentException("Not enough components: " + in);
}
+ assert(parent != null);
parent.fromString(in.substring(0, comma));
return decode(in.substring(comma + 1));
}
diff --git a/src/main/java/com/google/gwtorm/client/LongKey.java b/src/main/java/com/google/gwtorm/client/LongKey.java
index 0fc4ae6..986285e 100644
--- a/src/main/java/com/google/gwtorm/client/LongKey.java
+++ b/src/main/java/com/google/gwtorm/client/LongKey.java
@@ -37,6 +37,7 @@
/**
* @return the parent key instance; null if this is a root level key.
*/
+ @Override
public P getParentKey() {
return null;
}
@@ -72,6 +73,7 @@
return r.toString();
}
+ @Override
public void fromString(final String in) {
set(Long.parseLong(KeyUtil.parseFromString(getParentKey(), in)));
}
diff --git a/src/main/java/com/google/gwtorm/client/ShortKey.java b/src/main/java/com/google/gwtorm/client/ShortKey.java
index 59783f4..abda6d1 100644
--- a/src/main/java/com/google/gwtorm/client/ShortKey.java
+++ b/src/main/java/com/google/gwtorm/client/ShortKey.java
@@ -38,6 +38,7 @@
/**
* @return the parent key instance; null if this is a root level key.
*/
+ @Override
public P getParentKey() {
return null;
}
@@ -73,6 +74,7 @@
return r.toString();
}
+ @Override
public void fromString(final String in) {
set(Short.parseShort(KeyUtil.parseFromString(getParentKey(), in)));
}
diff --git a/src/main/java/com/google/gwtorm/client/StringKey.java b/src/main/java/com/google/gwtorm/client/StringKey.java
index 4c85d2a..a4dcf4c 100644
--- a/src/main/java/com/google/gwtorm/client/StringKey.java
+++ b/src/main/java/com/google/gwtorm/client/StringKey.java
@@ -38,6 +38,7 @@
/**
* @return the parent key instance; null if this is a root level key.
*/
+ @Override
public P getParentKey() {
return null;
}
@@ -79,6 +80,7 @@
return r.toString();
}
+ @Override
public void fromString(final String in) {
set(KeyUtil.parseFromString(getParentKey(), in));
}
diff --git a/src/main/java/com/google/gwtorm/jdbc/Database.java b/src/main/java/com/google/gwtorm/jdbc/Database.java
index 2c8d256..802bcec 100644
--- a/src/main/java/com/google/gwtorm/jdbc/Database.java
+++ b/src/main/java/com/google/gwtorm/jdbc/Database.java
@@ -117,6 +117,7 @@
* The JDBC exception detail should be examined to determine the root
* cause of the connection failure.
*/
+ @Override
public T open() throws OrmException {
return implFactory.open();
}
diff --git a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
index 00310e4..c7c5b5a 100644
--- a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
+++ b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
@@ -78,6 +78,7 @@
}
}
+ @Override
public void updateSchema(final StatementExecutor e) throws OrmException {
try {
createSequences(e);
@@ -171,6 +172,7 @@
return null;
}
+ @Override
public void pruneSchema(final StatementExecutor e) throws OrmException {
try {
pruneSequences(e);
@@ -234,6 +236,7 @@
return getDialect().nextLong(getConnection(), poolName);
}
+ @Override
public void close() {
if (conn != null) {
try {
diff --git a/src/main/java/com/google/gwtorm/jdbc/SimpleDataSource.java b/src/main/java/com/google/gwtorm/jdbc/SimpleDataSource.java
index 9bb96d2..a80a3a1 100644
--- a/src/main/java/com/google/gwtorm/jdbc/SimpleDataSource.java
+++ b/src/main/java/com/google/gwtorm/jdbc/SimpleDataSource.java
@@ -98,6 +98,7 @@
return sqle;
}
+ @Override
public Connection getConnection() throws SQLException {
if (driver != null) {
return driver.connect(url, connectionInfo);
@@ -105,6 +106,7 @@
return DriverManager.getConnection(url, connectionInfo);
}
+ @Override
public Connection getConnection(String user, String password)
throws SQLException {
if (driver != null) {
@@ -120,29 +122,36 @@
return DriverManager.getConnection(url, user, password);
}
+ @Override
public PrintWriter getLogWriter() {
return logWriter;
}
+ @Override
public void setLogWriter(final PrintWriter out) {
logWriter = out;
}
+ @Override
public int getLoginTimeout() {
return 0;
}
+ @Override
public void setLoginTimeout(int seconds) {
}
+ @Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
+ @Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException(getClass().getName() + " wraps nothing");
}
+ @Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
diff --git a/src/main/java/com/google/gwtorm/schema/Util.java b/src/main/java/com/google/gwtorm/schema/Util.java
index 1ca5e5d..6a55c72 100644
--- a/src/main/java/com/google/gwtorm/schema/Util.java
+++ b/src/main/java/com/google/gwtorm/schema/Util.java
@@ -41,9 +41,16 @@
r.append(c);
}
}
- return r.toString();
+ String friendlyName = r.toString();
+ if (friendlyName.length() > 30) {
+ throw new IllegalArgumentException(String.format(
+ "Identifier '%s' for name '%s' is greater than 30 characters",
+ friendlyName, name));
+ }
+ return friendlyName;
}
+
public static String any(final String a, final String b) {
if (a != null && a.length() > 0) {
return a;
diff --git a/src/main/java/com/google/gwtorm/schema/sql/DialectMaxDB.java b/src/main/java/com/google/gwtorm/schema/sql/DialectMaxDB.java
new file mode 100644
index 0000000..b1cbc07
--- /dev/null
+++ b/src/main/java/com/google/gwtorm/schema/sql/DialectMaxDB.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import com.google.gwtorm.schema.ColumnModel;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.StatementExecutor;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.HashSet;
+import java.util.Set;
+
+public class DialectMaxDB extends SqlDialect {
+
+ public DialectMaxDB() {
+ typeNames.put(Types.BIGINT, "FIXED (19,0)");
+ typeNames.put(Types.LONGVARCHAR, "LONG UNICODE");
+ }
+
+ @Override
+ public boolean canDetermineIndividualBatchUpdateCounts() {
+ return false;
+ }
+
+ @Override
+ public boolean canDetermineTotalBatchUpdateCount() {
+ return true;
+ }
+
+ @Override
+ public int executeBatch(PreparedStatement ps) throws SQLException {
+ ps.executeBatch();
+ return ps.getUpdateCount(); // total number of rows updated (on MaxDB)
+ }
+
+ @Override
+ public Set<String> listSequences(Connection conn) throws SQLException {
+ final Statement s = conn.createStatement();
+ try {
+ // lists sequences from schema associated with the current connection only
+ final ResultSet rs =
+ s.executeQuery("SELECT sequence_name FROM sequences");
+ try {
+ HashSet<String> sequences = new HashSet<String>();
+ while (rs.next()) {
+ sequences.add(rs.getString(1).toLowerCase());
+ }
+ return sequences;
+ } finally {
+ rs.close();
+ }
+ } finally {
+ s.close();
+ }
+ }
+
+ @Override
+ public void renameColumn(StatementExecutor e, String tableName,
+ String fromColumn, ColumnModel col) throws OrmException {
+ final StringBuilder s = new StringBuilder();
+ s.append("RENAME COLUMN ");
+ s.append(tableName).append(".").append(fromColumn);
+ s.append(" TO ");
+ s.append(col.getColumnName());
+ e.execute(s.toString());
+ }
+
+ @Override
+ public OrmException convertError(String op, String entity, SQLException err) {
+ int sqlstate = getSQLStateInt(err);
+ if (sqlstate == 23000) { // UNIQUE CONSTRAINT VIOLATION
+ int errorCode = err.getErrorCode();
+ if (errorCode == 200 || errorCode == -20) { // Duplicate Key
+ return new OrmDuplicateKeyException(entity, err);
+ }
+ }
+ return super.convertError(op, entity, err);
+ }
+
+ @Override
+ public String getNextSequenceValueSql(String seqname) {
+ return "SELECT " + seqname + ".nextval FROM dual";
+ }
+
+ @Override
+ public boolean handles(String url, Connection c) throws SQLException {
+ return url.startsWith("jdbc:sapdb:");
+ }
+
+ @Override
+ public void renameTable(StatementExecutor e, String from, String to)
+ throws OrmException {
+ final StringBuilder r = new StringBuilder();
+ r.append("RENAME TABLE ");
+ r.append(from);
+ r.append(" TO ");
+ r.append(to);
+ e.execute(r.toString());
+ }
+
+}
diff --git a/src/main/java/com/google/gwtorm/schema/sql/DialectOracle.java b/src/main/java/com/google/gwtorm/schema/sql/DialectOracle.java
index 63227be..827c656 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/DialectOracle.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/DialectOracle.java
@@ -19,6 +19,7 @@
import com.google.gwtorm.server.StatementExecutor;
import java.sql.Connection;
+import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@@ -53,7 +54,7 @@
try {
ResultSet rs = s.executeQuery("SELECT table_name FROM user_tables");
try {
- HashSet<String> tables = new HashSet<String>();
+ Set<String> tables = new HashSet<String>();
while (rs.next()) {
tables.add(rs.getString(1).toLowerCase());
}
@@ -67,12 +68,34 @@
}
@Override
+ public Set<String> listIndexes(final Connection db, String tableName)
+ throws SQLException {
+ PreparedStatement s = db.prepareStatement("SELECT distinct index_name"
+ + " FROM user_indexes WHERE table_name = ?");
+ try {
+ s.setString(1, tableName.toUpperCase());
+ ResultSet rs = s.executeQuery();
+ try {
+ Set<String> indexes = new HashSet<String>();
+ while (rs.next()) {
+ indexes.add(rs.getString(1).toLowerCase());
+ }
+ return indexes;
+ } finally {
+ rs.close();
+ }
+ } finally {
+ s.close();
+ }
+ }
+
+ @Override
public Set<String> listSequences(Connection db) throws SQLException {
Statement s = db.createStatement();
try {
ResultSet rs = s.executeQuery("SELECT sequence_name FROM user_sequences");
try {
- HashSet<String> sequences = new HashSet<String>();
+ Set<String> sequences = new HashSet<String>();
while (rs.next()) {
sequences.add(rs.getString(1).toLowerCase());
}
diff --git a/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java b/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
index ad1f16b..fdfe6c8 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
@@ -44,6 +44,7 @@
DIALECTS.add(new DialectPostgreSQL());
DIALECTS.add(new DialectMySQL());
DIALECTS.add(new DialectOracle());
+ DIALECTS.add(new DialectMaxDB());
}
public static void register(SqlDialect dialect) {
@@ -224,7 +225,7 @@
final String[] types = new String[] {"TABLE"};
final ResultSet rs = db.getMetaData().getTables(null, null, null, types);
try {
- HashSet<String> tables = new HashSet<String>();
+ Set<String> tables = new HashSet<String>();
while (rs.next()) {
tables.add(rs.getString("TABLE_NAME").toLowerCase());
}
@@ -254,6 +255,35 @@
}
/**
+ * List all indexes for the given table name.
+ *
+ * @param db connection to the schema.
+ * @param tableName the table to list indexes from, in lowercase.
+ * @return set of declared indexes, in lowercase.
+ * @throws SQLException the indexes cannot be listed.
+ */
+ public Set<String> listIndexes(final Connection db, String tableName)
+ throws SQLException {
+ final DatabaseMetaData meta = db.getMetaData();
+ if (meta.storesUpperCaseIdentifiers()) {
+ tableName = tableName.toUpperCase();
+ } else if (meta.storesLowerCaseIdentifiers()) {
+ tableName = tableName.toLowerCase();
+ }
+
+ ResultSet rs = meta.getIndexInfo(null, null, tableName, false, true);
+ try {
+ Set<String> indexes = new HashSet<String>();
+ while (rs.next()) {
+ indexes.add(rs.getString("INDEX_NAME").toLowerCase());
+ }
+ return indexes;
+ } finally {
+ rs.close();
+ }
+ }
+
+ /**
* List all sequences in the current database schema.
*
* @param db connection to the schema.
diff --git a/src/main/java/com/google/gwtorm/server/AbstractAccess.java b/src/main/java/com/google/gwtorm/server/AbstractAccess.java
index 8c6fd9f..f598630 100644
--- a/src/main/java/com/google/gwtorm/server/AbstractAccess.java
+++ b/src/main/java/com/google/gwtorm/server/AbstractAccess.java
@@ -32,6 +32,7 @@
// Do nothing by default.
}
+ @Override
public CheckedFuture<E, OrmException> getAsync(K key) {
try {
return Futures.immediateCheckedFuture(get(key));
@@ -40,6 +41,7 @@
}
}
+ @Override
public ResultSet<E> get(final Iterable<K> keys) throws OrmException {
final ArrayList<E> r = new ArrayList<E>();
for (final K key : keys) {
@@ -51,6 +53,7 @@
return new ListResultSet<E>(r);
}
+ @Override
public Map<K, E> toMap(final Iterable<E> c) {
try {
final HashMap<K, E> r = new HashMap<K, E>();
diff --git a/src/main/java/com/google/gwtorm/server/AbstractResultSet.java b/src/main/java/com/google/gwtorm/server/AbstractResultSet.java
index c17270b..5eb2803 100644
--- a/src/main/java/com/google/gwtorm/server/AbstractResultSet.java
+++ b/src/main/java/com/google/gwtorm/server/AbstractResultSet.java
@@ -45,6 +45,7 @@
};
}
+ @Override
public List<T> toList() {
List<T> r = new ArrayList<T>();
for (T obj : this) {
diff --git a/src/main/java/com/google/gwtorm/server/ListResultSet.java b/src/main/java/com/google/gwtorm/server/ListResultSet.java
index de6b224..642dd0c 100644
--- a/src/main/java/com/google/gwtorm/server/ListResultSet.java
+++ b/src/main/java/com/google/gwtorm/server/ListResultSet.java
@@ -26,10 +26,12 @@
items = r;
}
+ @Override
public Iterator<T> iterator() {
return toList().iterator();
}
+ @Override
public List<T> toList() {
if (items == null) {
throw new IllegalStateException("Results already obtained");
@@ -39,6 +41,7 @@
return r;
}
+ @Override
public void close() {
items = null;
}
diff --git a/src/main/java/com/google/gwtorm/server/ResultSet.java b/src/main/java/com/google/gwtorm/server/ResultSet.java
index cf589c5..18f8c75 100644
--- a/src/main/java/com/google/gwtorm/server/ResultSet.java
+++ b/src/main/java/com/google/gwtorm/server/ResultSet.java
@@ -30,6 +30,7 @@
* <code>hasNext()</code> returns false) {@link #close()} will be
* automatically called.
*/
+ @Override
Iterator<T> iterator();
/**
diff --git a/src/test/java/com/google/gwtorm/data/Person.java b/src/test/java/com/google/gwtorm/data/Person.java
index 4984802..38644da 100644
--- a/src/test/java/com/google/gwtorm/data/Person.java
+++ b/src/test/java/com/google/gwtorm/data/Person.java
@@ -67,6 +67,10 @@
return age;
}
+ public void setAge(int age) {
+ this.age = age;
+ }
+
public boolean isRegistered() {
return registered;
}
diff --git a/src/test/java/com/google/gwtorm/data/PersonAccess.java b/src/test/java/com/google/gwtorm/data/PersonAccess.java
index 091af8e..af36e7a 100644
--- a/src/test/java/com/google/gwtorm/data/PersonAccess.java
+++ b/src/test/java/com/google/gwtorm/data/PersonAccess.java
@@ -21,6 +21,7 @@
import com.google.gwtorm.server.ResultSet;
public interface PersonAccess extends Access<Person, Person.Key> {
+ @Override
@PrimaryKey("name")
Person get(Person.Key key) throws OrmException;
diff --git a/src/test/java/com/google/gwtorm/data/PersonAccess2.java b/src/test/java/com/google/gwtorm/data/PersonAccess2.java
index 46e12cd..a937ef9 100644
--- a/src/test/java/com/google/gwtorm/data/PersonAccess2.java
+++ b/src/test/java/com/google/gwtorm/data/PersonAccess2.java
@@ -19,6 +19,7 @@
import com.google.gwtorm.server.PrimaryKey;
public interface PersonAccess2 extends Access<Person2, Person.Key> {
+ @Override
@PrimaryKey("name")
Person2 get(Person.Key key) throws OrmException;
}
diff --git a/src/test/java/com/google/gwtorm/jdbc/AbstractTestJdbcAccess.java b/src/test/java/com/google/gwtorm/jdbc/AbstractTestJdbcAccess.java
index c3893cd..3fad032 100644
--- a/src/test/java/com/google/gwtorm/jdbc/AbstractTestJdbcAccess.java
+++ b/src/test/java/com/google/gwtorm/jdbc/AbstractTestJdbcAccess.java
@@ -459,6 +459,7 @@
super(s);
}
+ @Override
public String getRelationName() {
return "Data";
}
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java b/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java
index db674fa..ce37fbe 100644
--- a/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java
@@ -129,6 +129,21 @@
}
@Test
+ public void testListIndexes() throws OrmException, SQLException {
+ assertTrue(dialect.listTables(db).isEmpty());
+
+ execute("CREATE SEQUENCE cnt");
+ execute("CREATE TABLE foo (cnt INT, bar INT, baz INT)");
+ execute("CREATE UNIQUE INDEX FOO_PRIMARY_IND ON foo(cnt)");
+ execute("CREATE INDEX FOO_SECOND_IND ON foo(bar, baz)");
+
+ Set<String> s = dialect.listIndexes(db, "foo");
+ assertEquals(2, s.size());
+ assertTrue(s.contains("foo_primary_ind"));
+ assertTrue(s.contains("foo_second_ind"));
+ }
+
+ @Test
public void testUpgradeSchema() throws SQLException, OrmException {
final PhoneBookDb p = phoneBook.open();
try {
@@ -200,7 +215,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.rollback();
@@ -213,7 +228,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.commit();
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectMaxDBTest.java b/src/test/java/com/google/gwtorm/schema/sql/DialectMaxDBTest.java
new file mode 100644
index 0000000..9bab614
--- /dev/null
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectMaxDBTest.java
@@ -0,0 +1,358 @@
+// Copyright 2014 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.schema.sql;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNoException;
+
+import com.google.gwtorm.data.Address;
+import com.google.gwtorm.data.Person;
+import com.google.gwtorm.data.PhoneBookDb;
+import com.google.gwtorm.data.PhoneBookDb2;
+import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.jdbc.JdbcExecutor;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.jdbc.SimpleDataSource;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gwtorm.server.OrmException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+public class DialectMaxDBTest {
+ private static final String MAXDB_URL_KEY = "maxdb.url";
+ private static final String MAXDB_USER_KEY = "maxdb.user";
+ private static final String MAXDB_PASSWORD_KEY = "maxdb.password";
+ private static final String MAXDB_DRIVER = "com.sap.dbtech.jdbc.DriverSapDB";
+ private Connection db;
+ private JdbcExecutor executor;
+ private SqlDialect dialect;
+ private Database<PhoneBookDb> phoneBook;
+ private Database<PhoneBookDb2> phoneBook2;
+
+ @Before
+ public void setUp() throws Exception {
+ try {
+ Class.forName(MAXDB_DRIVER);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+
+ final String url = System.getProperty(MAXDB_URL_KEY);
+ final String user = System.getProperty(MAXDB_USER_KEY);
+ final String pass = System.getProperty(MAXDB_PASSWORD_KEY);
+
+ db = DriverManager.getConnection(url, user, pass);
+ executor = new JdbcExecutor(db);
+ dialect = new DialectMaxDB().refine(db);
+
+ final Properties p = new Properties();
+ p.setProperty("driver", MAXDB_DRIVER);
+ p.setProperty("url", db.getMetaData().getURL());
+ p.setProperty("user", user);
+ p.setProperty("password", pass);
+ phoneBook =
+ new Database<PhoneBookDb>(new SimpleDataSource(p), PhoneBookDb.class);
+ phoneBook2 =
+ new Database<PhoneBookDb2>(new SimpleDataSource(p), PhoneBookDb2.class);
+ }
+
+ private void drop(String drop) {
+ try {
+ execute("DROP " + drop);
+ } catch (OrmException e) {
+ }
+ }
+
+ @After
+ public void tearDown() {
+ // Database content must be flushed because
+ // tests assume that the database is empty
+ drop("SEQUENCE address_id");
+ drop("SEQUENCE cnt");
+
+ drop("TABLE addresses");
+ drop("TABLE foo");
+ drop("TABLE bar");
+ drop("TABLE people");
+
+ if (executor != null) {
+ executor.close();
+ }
+ executor = null;
+
+ if (db != null) {
+ try {
+ db.close();
+ } catch (SQLException e) {
+ throw new RuntimeException("Cannot close database", e);
+ }
+ }
+ db = null;
+ }
+
+ private void execute(final String sql) throws OrmException {
+ executor.execute(sql);
+ }
+
+ @Test
+ public void testListSequences() throws OrmException, SQLException {
+ assertTrue(dialect.listSequences(db).isEmpty());
+
+ execute("CREATE SEQUENCE cnt");
+ execute("CREATE TABLE foo (cnt INT)");
+
+ Set<String> s = dialect.listSequences(db);
+ assertEquals(1, s.size());
+ assertTrue(s.contains("cnt"));
+ assertFalse(s.contains("foo"));
+ }
+
+ @Test
+ public void testListTables() throws OrmException, SQLException {
+ assertTrue(dialect.listTables(db).isEmpty());
+
+ execute("CREATE SEQUENCE cnt");
+ execute("CREATE TABLE foo (cnt INT)");
+
+ Set<String> s = dialect.listTables(db);
+ assertEquals(1, s.size());
+ assertFalse(s.contains("cnt"));
+ assertTrue(s.contains("foo"));
+ }
+
+ @Test
+ public void testListIndexes() throws OrmException, SQLException {
+ assertTrue(dialect.listTables(db).isEmpty());
+
+ execute("CREATE SEQUENCE cnt");
+ execute("CREATE TABLE foo (cnt INT, bar INT, baz INT)");
+ execute("CREATE UNIQUE INDEX FOO_PRIMARY_IND ON foo(cnt)");
+ execute("CREATE INDEX FOO_SECOND_IND ON foo(bar, baz)");
+
+ Set<String> s = dialect.listIndexes(db, "foo");
+ assertEquals(2, s.size());
+ assertTrue(s.contains("foo_primary_ind"));
+ assertTrue(s.contains("foo_second_ind"));
+ }
+
+ @Test
+ public void testUpgradeSchema() throws SQLException, OrmException {
+ final PhoneBookDb p = phoneBook.open();
+ try {
+ p.updateSchema(executor);
+
+ execute("CREATE SEQUENCE cnt");
+ execute("CREATE TABLE foo (cnt INT)");
+
+ execute("ALTER TABLE people ADD fake_name VARCHAR(20)");
+ execute("ALTER TABLE people DROP COLUMN registered");
+ execute("DROP TABLE addresses");
+ execute("DROP SEQUENCE address_id");
+
+ Set<String> sequences, tables;
+
+ p.updateSchema(executor);
+ sequences = dialect.listSequences(db);
+ tables = dialect.listTables(db);
+ assertTrue(sequences.contains("cnt"));
+ assertTrue(tables.contains("foo"));
+
+ assertTrue(sequences.contains("address_id"));
+ assertTrue(tables.contains("addresses"));
+
+ p.pruneSchema(executor);
+ sequences = dialect.listSequences(db);
+ tables = dialect.listTables(db);
+ assertFalse(sequences.contains("cnt"));
+ assertFalse(tables.contains("foo"));
+
+ final Person.Key pk = new Person.Key("Bob");
+ final Person bob = new Person(pk, p.nextAddressId());
+ p.people().insert(asList(bob));
+
+ final Address addr =
+ new Address(new Address.Key(pk, "home"), "some place");
+ p.addresses().insert(asList(addr));
+ } finally {
+ p.close();
+ }
+
+ final PhoneBookDb2 p2 = phoneBook2.open();
+ try {
+ ((JdbcSchema) p2).renameField(executor, "people", "registered",
+ "isRegistered");
+ } finally {
+ p2.close();
+ }
+ }
+
+ @Test
+ public void testRenameTable() throws SQLException, OrmException {
+ assertTrue(dialect.listTables(db).isEmpty());
+ execute("CREATE TABLE foo (cnt INT)");
+ Set<String> s = dialect.listTables(db);
+ assertEquals(1, s.size());
+ assertTrue(s.contains("foo"));
+ final PhoneBookDb p = phoneBook.open();
+ try {
+ ((JdbcSchema) p).renameTable(executor, "foo", "bar");
+ } finally {
+ p.close();
+ }
+ s = dialect.listTables(db);
+ assertTrue(s.contains("bar"));
+ assertFalse(s.contains("for"));
+ }
+
+ @Test
+ public void testInsert() throws OrmException {
+ final PhoneBookDb p = phoneBook.open();
+ try {
+ p.updateSchema(executor);
+
+ final Person.Key pk = new Person.Key("Bob");
+ final Person bob = new Person(pk, p.nextAddressId());
+ p.people().insert(asList(bob));
+
+ try {
+ p.people().insert(asList(bob));
+ fail();
+ } catch (OrmDuplicateKeyException duprec) {
+ // expected
+ }
+ } finally {
+ p.close();
+ }
+ }
+
+ @Test
+ public void testConstraintViolationOnIndex() throws OrmException {
+ final PhoneBookDb p = phoneBook.open();
+ try {
+ p.updateSchema(executor);
+
+ execute("CREATE UNIQUE INDEX idx ON people (age)");
+ try {
+
+ final Person.Key pk = new Person.Key("Bob");
+ final Person bob = new Person(pk, p.nextAddressId());
+ bob.setAge(40);
+ p.people().insert(asList(bob));
+
+ final Person.Key joePk = new Person.Key("Joe");
+ Person joe = new Person(joePk, p.nextAddressId());
+ joe.setAge(40);
+ try {
+ p.people().insert(asList(joe));
+ fail();
+ } catch (OrmDuplicateKeyException duprec) {
+ fail();
+ } catch (OrmException noDuprec) {
+ // expeceted
+ }
+ } finally {
+ execute("DROP INDEX idx ON people");
+ }
+ } finally {
+ p.close();
+ }
+ }
+
+ @Test
+ public void testUpdate() throws OrmException {
+ final PhoneBookDb p = phoneBook.open();
+ try {
+ p.updateSchema(executor);
+
+ final Person.Key pk = new Person.Key("Bob");
+ Person bob = new Person(pk, p.nextAddressId());
+ bob.setAge(40);
+ p.people().insert(asList(bob));
+
+ bob.setAge(50);
+ p.people().update(asList(bob));
+
+ bob = p.people().get(pk);
+ assertEquals(50, bob.age());
+ } finally {
+ p.close();
+ }
+ }
+
+ @Test
+ public void testUpsert() throws OrmException {
+ final PhoneBookDb p = phoneBook.open();
+ try {
+ p.updateSchema(executor);
+
+ final Person.Key bobPk = new Person.Key("Bob");
+ Person bob = new Person(bobPk, p.nextAddressId());
+ bob.setAge(40);
+ p.people().insert(asList(bob));
+
+ final Person.Key joePk = new Person.Key("Joe");
+ Person joe = new Person(joePk, p.nextAddressId());
+ bob.setAge(50);
+ p.people().upsert(asList(bob, joe));
+
+ bob = p.people().get(bobPk);
+ assertEquals(50, bob.age());
+ assertNotNull(p.people().get(joePk));
+ } finally {
+ p.close();
+ }
+ }
+
+ @Test
+ public void testRollbackTransaction() throws SQLException, OrmException {
+ PhoneBookDb schema = phoneBook.open();
+ schema.updateSchema(executor);
+ schema.people().beginTransaction(null);
+ ArrayList<Person> all = new ArrayList<>();
+ all.add(new Person(new Person.Key("Bob"), 18));
+ schema.people().insert(all);
+ schema.rollback();
+ List<Person> r = schema.people().olderThan(10).toList();
+ assertEquals(0, r.size());
+ }
+
+ @Test
+ public void testCommitTransaction() throws SQLException, OrmException {
+ PhoneBookDb schema = phoneBook.open();
+ schema.updateSchema(executor);
+ schema.people().beginTransaction(null);
+ ArrayList<Person> all = new ArrayList<>();
+ all.add(new Person(new Person.Key("Bob"), 18));
+ schema.people().insert(all);
+ schema.commit();
+ List<Person> r = schema.people().olderThan(10).toList();
+ assertEquals(1, r.size());
+ }
+}
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java b/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java
index 186134a..b0c8db5 100644
--- a/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java
@@ -143,6 +143,20 @@
}
@Test
+ public void testListIndexes() throws OrmException, SQLException {
+ assertTrue(dialect.listTables(db).isEmpty());
+
+ execute("CREATE TABLE foo (cnt INT, bar INT, baz INT)");
+ execute("CREATE UNIQUE INDEX FOO_PRIMARY_IND ON foo(cnt)");
+ execute("CREATE INDEX FOO_SECOND_IND ON foo(bar, baz)");
+
+ Set<String> s = dialect.listIndexes(db, "foo");
+ assertEquals(2, s.size());
+ assertTrue(s.contains("foo_primary_ind"));
+ assertTrue(s.contains("foo_second_ind"));
+ }
+
+ @Test
public void testUpgradeSchema() throws SQLException, OrmException {
final PhoneBookDb p = phoneBook.open();
try {
@@ -216,7 +230,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.rollback();
@@ -229,7 +243,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.commit();
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectOracleSQLTest.java b/src/test/java/com/google/gwtorm/schema/sql/DialectOracleSQLTest.java
index 8f75bb1..bb4d7fb 100644
--- a/src/test/java/com/google/gwtorm/schema/sql/DialectOracleSQLTest.java
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectOracleSQLTest.java
@@ -143,6 +143,21 @@
}
@Test
+ public void testListIndexes() throws OrmException, SQLException {
+ assertTrue(dialect.listTables(db).isEmpty());
+
+ execute("CREATE SEQUENCE cnt");
+ execute("CREATE TABLE foo (cnt INT, bar INT, baz INT)");
+ execute("CREATE UNIQUE INDEX FOO_PRIMARY_IND ON foo(cnt)");
+ execute("CREATE INDEX FOO_SECOND_IND ON foo(bar, baz)");
+
+ Set<String> s = dialect.listIndexes(db, "foo");
+ assertEquals(2, s.size());
+ assertTrue(s.contains("foo_primary_ind"));
+ assertTrue(s.contains("foo_second_ind"));
+ }
+
+ @Test
public void testUpgradeSchema() throws SQLException, OrmException {
final PhoneBookDb p = phoneBook.open();
try {
@@ -216,7 +231,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.rollback();
@@ -229,7 +244,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.commit();
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java b/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java
index 0330b18..aae302f 100644
--- a/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java
@@ -142,6 +142,20 @@
}
@Test
+ public void testListIndexes() throws OrmException, SQLException {
+ assertTrue(dialect.listTables(db).isEmpty());
+
+ execute("CREATE TABLE foo (cnt INT, bar INT, baz INT)");
+ execute("CREATE UNIQUE INDEX FOO_PRIMARY_IND ON foo(cnt)");
+ execute("CREATE INDEX FOO_SECOND_IND ON foo(bar, baz)");
+
+ Set<String> s = dialect.listIndexes(db, "foo");
+ assertEquals(2, s.size());
+ assertTrue(s.contains("foo_primary_ind"));
+ assertTrue(s.contains("foo_second_ind"));
+ }
+
+ @Test
public void testUpgradeSchema() throws SQLException, OrmException {
final PhoneBookDb p = phoneBook.open();
try {
@@ -215,7 +229,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.rollback();
@@ -228,7 +242,7 @@
PhoneBookDb schema = phoneBook.open();
schema.updateSchema(executor);
schema.people().beginTransaction(null);
- ArrayList<Person> all = new ArrayList<Person>();
+ ArrayList<Person> all = new ArrayList<>();
all.add(new Person(new Person.Key("Bob"), 18));
schema.people().insert(all);
schema.commit();