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();