diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java index f8bb0d7fd..c2f58de98 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java @@ -393,8 +393,13 @@ public class AbstractColQuery> extends close(); } } - - - + + public Object[] uniqueResult(Expr first, Expr second, Expr... rest) { + try { + return super.uniqueResult(first, second, rest); + } finally { + close(); + } + } } diff --git a/querydsl-core/src/main/java/com/mysema/query/Projectable.java b/querydsl-core/src/main/java/com/mysema/query/Projectable.java index f53dd0a89..c316ff0ad 100644 --- a/querydsl-core/src/main/java/com/mysema/query/Projectable.java +++ b/querydsl-core/src/main/java/com/mysema/query/Projectable.java @@ -111,6 +111,15 @@ public interface Projectable { * @return the result or null for an empty result */ RT uniqueResult(Expr projection); + + /** + * + * @param first + * @param second + * @param rest + * @return + */ + Object[] uniqueResult(Expr first, Expr second, Expr... rest); } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/ProjectableAdapter.java b/querydsl-core/src/main/java/com/mysema/query/ProjectableAdapter.java index c3a957464..bd3fddac3 100644 --- a/querydsl-core/src/main/java/com/mysema/query/ProjectableAdapter.java +++ b/querydsl-core/src/main/java/com/mysema/query/ProjectableAdapter.java @@ -72,6 +72,10 @@ public abstract class ProjectableAdapter implements Projectable{ return projectable.uniqueResult(expr); } + public Object[]uniqueResult(Expr first, Expr second, Expr... rest) { + return projectable.uniqueResult(first, second, rest); + } + public void setProjectable(Projectable projectable) { this.projectable = Assert.notNull(projectable); } diff --git a/querydsl-core/src/main/java/com/mysema/query/QueryBaseWithProjection.java b/querydsl-core/src/main/java/com/mysema/query/QueryBaseWithProjection.java index eca8d89c7..84e465800 100644 --- a/querydsl-core/src/main/java/com/mysema/query/QueryBaseWithProjection.java +++ b/querydsl-core/src/main/java/com/mysema/query/QueryBaseWithProjection.java @@ -72,4 +72,9 @@ public abstract class QueryBaseWithProjection it = iterate(expr); return it.hasNext() ? it.next() : null; } + + public Object[] uniqueResult(Expr first, Expr second, Expr... rest) { + Iterator it = iterate(first, second, rest); + return it.hasNext() ? it.next() : null; + } } diff --git a/querydsl-sql/pom.xml b/querydsl-sql/pom.xml index 9de9029a1..60799682b 100644 --- a/querydsl-sql/pom.xml +++ b/querydsl-sql/pom.xml @@ -33,6 +33,13 @@ test + + org.apache.derby + derby + 10.4.2.0 + test + + mysql mysql-connector-java diff --git a/querydsl-sql/src/main/java/com/mysema/query/grammar/Dialect.java b/querydsl-sql/src/main/java/com/mysema/query/grammar/Dialect.java index 8e34ed2ad..4432067d1 100644 --- a/querydsl-sql/src/main/java/com/mysema/query/grammar/Dialect.java +++ b/querydsl-sql/src/main/java/com/mysema/query/grammar/Dialect.java @@ -14,6 +14,7 @@ import java.math.BigInteger; * Querydsl SQL supports the following database systems : *
    *
  • HSQLDB
  • + *
  • Derby
  • *
  • MySQL
  • *
  • Oracle
  • *
  • PostgreSQL
  • @@ -26,13 +27,35 @@ import java.math.BigInteger; public class Dialect { // tested - public static SqlOps forHqlsdb(){ + public static SqlOps forHSQLDB(){ return new SqlOps(){{ add(Ops.OpMath.ROUND, "round(%s,0)"); add(Ops.TRIM, "trim(both from %s)"); }}; } + // tested + public static SqlOps forDerby(){ + return new SqlOps(){{ + add(Ops.CONCAT, "%s || %s"); + add(Ops.OpMath.ROUND, "floor(%s)"); + add(Ops.SUBSTR1ARG, "substr(%s,%s+1)"); + add(Ops.SUBSTR2ARGS, "substr(%s,%s+1,%s+1)"); + + add(Ops.STARTSWITH, "%s like (%s || '%%')"); + add(Ops.ENDSWITH, "%s like ('%%' || %s)"); + add(Ops.STARTSWITH_IC, "lower(%s) like (lower(%s) || '%%')"); + add(Ops.ENDSWITH_IC, "lower(%s) like ('%%' || lower(%s))"); + + add(Ops.OpDateTime.YEAR, "year(%s)"); + add(Ops.OpDateTime.MONTH, "month(%s)"); + + add(Ops.OpDateTime.HOUR, "hour(%s)"); + add(Ops.OpDateTime.MINUTE, "minute(%s)"); + add(Ops.OpDateTime.SECOND, "second(%s)"); + }}; + } + // tested public static SqlOps forMySQL(){ return new SqlOps(){{ diff --git a/querydsl-sql/src/main/java/com/mysema/query/grammar/SqlOps.java b/querydsl-sql/src/main/java/com/mysema/query/grammar/SqlOps.java index c0dcdd48f..1c8918664 100644 --- a/querydsl-sql/src/main/java/com/mysema/query/grammar/SqlOps.java +++ b/querydsl-sql/src/main/java/com/mysema/query/grammar/SqlOps.java @@ -75,8 +75,8 @@ public class SqlOps extends OperationPatterns { add(Ops.OpDateTime.CURRENT_TIME, "current_timestamp"); // string - add(Ops.SUBSTR1ARG, "locate(%s,%s) > 0"); - add(Ops.SUBSTR2ARGS, "substr(%s,%s,%s)"); + add(Ops.SUBSTR1ARG, "substr(%s,%s)"); + add(Ops.SUBSTR2ARGS,"substr(%s,%s,%s)"); add(Ops.STARTSWITH, "%s like concat(%s,'%%')"); add(Ops.ENDSWITH, "%s like concat('%%',%s)"); diff --git a/querydsl-sql/src/test/java/com/mysema/query/ExcludeIn.java b/querydsl-sql/src/test/java/com/mysema/query/ExcludeIn.java index e0cefb0b4..595533b15 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/ExcludeIn.java +++ b/querydsl-sql/src/test/java/com/mysema/query/ExcludeIn.java @@ -21,5 +21,5 @@ import java.lang.annotation.Target; @Target(ElementType.METHOD) @Inherited public @interface ExcludeIn { - String value(); + String[] value(); } diff --git a/querydsl-sql/src/test/java/com/mysema/query/FilteringTestRunner.java b/querydsl-sql/src/test/java/com/mysema/query/FilteringTestRunner.java index 9b51a5485..2bec053f6 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/FilteringTestRunner.java +++ b/querydsl-sql/src/test/java/com/mysema/query/FilteringTestRunner.java @@ -6,6 +6,7 @@ package com.mysema.query; import java.lang.reflect.Method; +import java.util.Arrays; import org.junit.internal.runners.InitializationError; import org.junit.internal.runners.JUnit4ClassRunner; @@ -61,8 +62,8 @@ public class FilteringTestRunner extends JUnit4ClassRunner{ if (label != null){ ExcludeIn ex = method.getAnnotation(ExcludeIn.class); IncludeIn in = method.getAnnotation(IncludeIn.class); - ignore |= ex != null && ex.value().contains(label); - ignore |= in != null && !in.value().contains(label); + ignore |= ex != null && Arrays.asList(ex.value()).contains(label); + ignore |= in != null && !Arrays.asList(in.value()).contains(label); } return ignore; } diff --git a/querydsl-sql/src/test/java/com/mysema/query/IncludeIn.java b/querydsl-sql/src/test/java/com/mysema/query/IncludeIn.java index 90c340bda..faf29def8 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/IncludeIn.java +++ b/querydsl-sql/src/test/java/com/mysema/query/IncludeIn.java @@ -21,5 +21,5 @@ import java.lang.annotation.Target; @Target(ElementType.METHOD) @Inherited public @interface IncludeIn { - String value(); + String[] value(); } diff --git a/querydsl-sql/src/test/java/com/mysema/query/sql/DerbyTest.java b/querydsl-sql/src/test/java/com/mysema/query/sql/DerbyTest.java new file mode 100644 index 000000000..fa639caaf --- /dev/null +++ b/querydsl-sql/src/test/java/com/mysema/query/sql/DerbyTest.java @@ -0,0 +1,107 @@ +package com.mysema.query.sql; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; + +import com.mysema.query.FilteringTestRunner; +import com.mysema.query.Label; +import com.mysema.query.grammar.Dialect; + +@RunWith(FilteringTestRunner.class) +@Label("derby") +public class DerbyTest extends SqlQueryTest { + + @BeforeClass + public static void setUp() throws Exception{ + String sql; + Connection c = getDerbyConnection(); + Statement stmt = c.createStatement(); + + connHolder.set(c); + stmtHolder.set(stmt); + + // survey +// stmt.execute("drop table survey if exists"); + safeExecute(stmt, "drop table survey"); + stmt.execute("create table survey (id int,name varchar(30))"); + stmt.execute("insert into survey values (1, 'Hello World')"); + + // test +// stmt.execute("drop table test if exists"); + safeExecute(stmt, "drop table test"); + stmt.execute("create table test(name varchar(255))"); + sql = "insert into test values(?)"; + PreparedStatement pstmt = c.prepareStatement(sql); + for (int i = 0; i < 10000; i++) { + pstmt.setString(1, "name" + i); + pstmt.addBatch(); + } + pstmt.executeBatch(); + + // employee +// stmt.execute("drop table employee if exists"); + safeExecute(stmt, "drop table employee"); + stmt.execute("create table employee(id int, " + + "firstname VARCHAR(50), " + "lastname VARCHAR(50), " + + "salary decimal(10, 2), " + "superior_id int, " + + "CONSTRAINT PK_employee PRIMARY KEY (id), " + + "CONSTRAINT FK_superior FOREIGN KEY (superior_id) " + + "REFERENCES employee(ID))"); + addEmployee(1, "Mike", "Smith", 160000, -1); + addEmployee(2, "Mary", "Smith", 140000, -1); + + // Employee under Mike + addEmployee(10, "Joe", "Divis", 50000, 1); + addEmployee(11, "Peter", "Mason", 45000, 1); + addEmployee(12, "Steve", "Johnson", 40000, 1); + addEmployee(13, "Jim", "Hood", 35000, 1); + + // Employee under Mike + addEmployee(20, "Jennifer", "Divis", 60000, 2); + addEmployee(21, "Helen", "Mason", 50000, 2); + addEmployee(22, "Daisy", "Johnson", 40000, 2); + addEmployee(23, "Barbara", "Hood", 30000, 2); + + // date_test and time_test +// stmt.execute("drop table time_test if exists"); +// stmt.execute("drop table date_test if exists"); + safeExecute(stmt, "drop table time_test"); + safeExecute(stmt, "drop table date_test"); + stmt.execute("create table time_test(time_test time)"); + stmt.execute("create table date_test(date_test date)"); + } + + private static void safeExecute(Statement stmt, String sql){ + try { + stmt.execute(sql); + } catch (SQLException e) { + // do nothing + } + } + + @Before + public void setUpForTest(){ + dialect = Dialect.forDerby().newLineToSingleSpace(); + } + + private static void addEmployee(int id, String firstName, String lastName, double salary, + int superiorId) throws Exception { + stmtHolder.get().execute("insert into employee values(" + id + ", '" + firstName + + "', '" + lastName + "', " + salary + ", " + + (superiorId <= 0 ? "null" : ("" + superiorId)) + ")"); + } + + private static Connection getDerbyConnection() throws Exception{ + Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); + String url = "jdbc:derby:demoDB;create=true"; + return DriverManager.getConnection(url, "", ""); + } + +} diff --git a/querydsl-sql/src/test/java/com/mysema/query/sql/HsqldbTest.java b/querydsl-sql/src/test/java/com/mysema/query/sql/HsqldbTest.java index 23cc9fdba..9ffbc6756 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/sql/HsqldbTest.java +++ b/querydsl-sql/src/test/java/com/mysema/query/sql/HsqldbTest.java @@ -85,7 +85,7 @@ public class HsqldbTest extends SqlQueryTest { @Before public void setUpForTest(){ - dialect = Dialect.forHqlsdb().newLineToSingleSpace(); + dialect = Dialect.forHSQLDB().newLineToSingleSpace(); } private static void addEmployee(int id, String firstName, String lastName, double salary, diff --git a/querydsl-sql/src/test/java/com/mysema/query/sql/SqlQueryTest.java b/querydsl-sql/src/test/java/com/mysema/query/sql/SqlQueryTest.java index 772b5a6d7..4f23ffb2e 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/sql/SqlQueryTest.java +++ b/querydsl-sql/src/test/java/com/mysema/query/sql/SqlQueryTest.java @@ -137,14 +137,14 @@ public abstract class SqlQueryTest { } @Test - @ExcludeIn("oracle") + @ExcludeIn({"oracle","derby"}) public void testSelectBooleanExpr() throws SQLException{ // TODO : FIXME System.out.println(q().from(survey).list(survey.id.eq(0))); } @Test - @ExcludeIn("oracle") + @ExcludeIn({"oracle","derby"}) public void testSelectBooleanExpr2() throws SQLException{ // TODO : FIXME System.out.println(q().from(survey).list(survey.id.gt(0))); @@ -254,7 +254,7 @@ public abstract class SqlQueryTest { } @Test - @ExcludeIn("oracle") + @ExcludeIn({"oracle","derby"}) public void testLimitAndOffset() throws SQLException{ // limit offset expectedQuery = "select employee.id from employee employee limit 4 offset 3"; @@ -326,7 +326,7 @@ public abstract class SqlQueryTest { } @Test - @ExcludeIn("hsqldb") + @ExcludeIn({"hsqldb","derby"}) public void testQueryWithoutFrom() throws SQLException{ q().list(add(new Expr.EConstant(1),1)); } @@ -339,6 +339,7 @@ public abstract class SqlQueryTest { } @Test + @ExcludeIn({"derby"}) public void testMathFunctions() throws SQLException{ Expr i = new Expr.EConstant(1); Expr d = new Expr.EConstant(1.0); @@ -367,6 +368,7 @@ public abstract class SqlQueryTest { } @Test + @ExcludeIn({"derby"}) public void testStringFunctions() throws SQLException{ EString s = employee.firstname; for (EString e : Arrays.asList( @@ -395,6 +397,7 @@ public abstract class SqlQueryTest { } @Test + @ExcludeIn({"derby"}) public void testDateTimeFunctions() throws SQLException{ Expr d = new Expr.EConstant(new Date()); Expr