From d756297151d1a3e70a9c99b56c8ff2f58d1ad92a Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Sat, 22 Dec 2012 20:39:49 +0100 Subject: [PATCH] [#2044] Add various TableRecord.fetchParent(...), fetchChild(...) and fetchChildren(...) methods to follow foreign key relationships --- jOOQ-test/src/org/jooq/test/ASETest.java | 12 ++ jOOQ-test/src/org/jooq/test/BaseTest.java | 19 +++ jOOQ-test/src/org/jooq/test/CUBRIDTest.java | 12 ++ jOOQ-test/src/org/jooq/test/DB2Test.java | 12 ++ jOOQ-test/src/org/jooq/test/DerbyTest.java | 12 ++ jOOQ-test/src/org/jooq/test/FirebirdTest.java | 12 ++ jOOQ-test/src/org/jooq/test/H2Test.java | 12 ++ jOOQ-test/src/org/jooq/test/HSQLDBTest.java | 12 ++ jOOQ-test/src/org/jooq/test/HSQLDBTest2.java | 12 ++ jOOQ-test/src/org/jooq/test/IngresTest.java | 12 ++ jOOQ-test/src/org/jooq/test/MySQLTest.java | 14 +- .../org/jooq/test/MySQLTestSchemaRewrite.java | 12 ++ jOOQ-test/src/org/jooq/test/OracleTest.java | 12 ++ jOOQ-test/src/org/jooq/test/PostgresTest.java | 12 ++ .../src/org/jooq/test/SQLServerTest.java | 12 ++ jOOQ-test/src/org/jooq/test/SQLiteTest.java | 12 ++ jOOQ-test/src/org/jooq/test/SybaseTest.java | 12 ++ .../test/_/testcases/ReferentialTests.java | 112 ++++++++++++++ .../src/org/jooq/test/jOOQAbstractTest.java | 9 ++ jOOQ/src/main/java/org/jooq/ForeignKey.java | 82 ++++++++++- jOOQ/src/main/java/org/jooq/TableRecord.java | 15 ++ .../main/java/org/jooq/UpdatableRecord.java | 30 +++- .../src/main/java/org/jooq/impl/Executor.java | 21 +-- .../java/org/jooq/impl/ReferenceImpl.java | 139 +++++++++++++++++- .../java/org/jooq/impl/TableRecordImpl.java | 8 + .../org/jooq/impl/UpdatableRecordImpl.java | 14 ++ jOOQ/src/main/java/org/jooq/impl/Utils.java | 64 ++++++-- 27 files changed, 668 insertions(+), 39 deletions(-) create mode 100644 jOOQ-test/src/org/jooq/test/_/testcases/ReferentialTests.java diff --git a/jOOQ-test/src/org/jooq/test/ASETest.java b/jOOQ-test/src/org/jooq/test/ASETest.java index 19ce79864e..4017ca9306 100644 --- a/jOOQ-test/src/org/jooq/test/ASETest.java +++ b/jOOQ-test/src/org/jooq/test/ASETest.java @@ -46,6 +46,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -62,6 +63,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.ase.generatedclasses.Keys; import org.jooq.test.ase.generatedclasses.tables.TAuthor; import org.jooq.test.ase.generatedclasses.tables.TBook; import org.jooq.test.ase.generatedclasses.tables.TBookStore; @@ -215,6 +217,16 @@ public class ASETest extends jOOQAbstractTest< return TBookToBookStore.STOCK; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table T725() { return T_725LobTest.T_725_LOB_TEST; diff --git a/jOOQ-test/src/org/jooq/test/BaseTest.java b/jOOQ-test/src/org/jooq/test/BaseTest.java index 8209f29021..320ed6828e 100644 --- a/jOOQ-test/src/org/jooq/test/BaseTest.java +++ b/jOOQ-test/src/org/jooq/test/BaseTest.java @@ -45,12 +45,17 @@ import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import junit.framework.Assert; + import org.jooq.ArrayRecord; import org.jooq.DAO; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Record1; import org.jooq.Record2; @@ -466,6 +471,14 @@ public abstract class BaseTest< return delegate.TBook_STATUS(); } + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return delegate.FK_T_BOOK_AUTHOR_ID(); + } + + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return delegate.FK_T_BOOK_CO_AUTHOR_ID(); + } + protected UpdatableTable TBookStore() { return delegate.TBookStore(); } @@ -844,4 +857,10 @@ public abstract class BaseTest< // Dummy parameters for SQL Server protected static Integer DUMMY_OUT_INT = new Integer(0); + + protected static void assertSame(Collection expected, Collection actual) { + if (!new HashSet(expected).equals(new HashSet(actual))) { + Assert.fail("Collections aren't the same : " + expected + " and " + actual); + } + } } diff --git a/jOOQ-test/src/org/jooq/test/CUBRIDTest.java b/jOOQ-test/src/org/jooq/test/CUBRIDTest.java index 5af62ea067..882b4d65e6 100644 --- a/jOOQ-test/src/org/jooq/test/CUBRIDTest.java +++ b/jOOQ-test/src/org/jooq/test/CUBRIDTest.java @@ -47,6 +47,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -63,6 +64,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.cubrid.generatedclasses.Keys; import org.jooq.test.cubrid.generatedclasses.Sequences; import org.jooq.test.cubrid.generatedclasses.tables.TAuthor; import org.jooq.test.cubrid.generatedclasses.tables.TBook; @@ -484,6 +486,16 @@ public class CUBRIDTest extends jOOQAbstractTest< return TBook.STATUS; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return VLibrary.V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/DB2Test.java b/jOOQ-test/src/org/jooq/test/DB2Test.java index 3ba4ce2aaf..64dc426124 100644 --- a/jOOQ-test/src/org/jooq/test/DB2Test.java +++ b/jOOQ-test/src/org/jooq/test/DB2Test.java @@ -51,6 +51,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -67,6 +68,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.db2.generatedclasses.Keys; import org.jooq.test.db2.generatedclasses.Routines; import org.jooq.test.db2.generatedclasses.Sequences; import org.jooq.test.db2.generatedclasses.tables.TAuthor; @@ -497,6 +499,16 @@ public class DB2Test extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return VLibrary.V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/DerbyTest.java b/jOOQ-test/src/org/jooq/test/DerbyTest.java index c4fc30cf85..ce7e2af1de 100644 --- a/jOOQ-test/src/org/jooq/test/DerbyTest.java +++ b/jOOQ-test/src/org/jooq/test/DerbyTest.java @@ -52,6 +52,7 @@ import java.sql.Timestamp; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -68,6 +69,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.derby.generatedclasses.Keys; import org.jooq.test.derby.generatedclasses.Sequences; import org.jooq.test.derby.generatedclasses.tables.TAuthor; import org.jooq.test.derby.generatedclasses.tables.TBook; @@ -499,6 +501,16 @@ public class DerbyTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return VLibrary.V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/FirebirdTest.java b/jOOQ-test/src/org/jooq/test/FirebirdTest.java index 115094e5c4..1daabd8f22 100644 --- a/jOOQ-test/src/org/jooq/test/FirebirdTest.java +++ b/jOOQ-test/src/org/jooq/test/FirebirdTest.java @@ -57,6 +57,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -73,6 +74,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.firebird.generatedclasses.Keys; import org.jooq.test.firebird.generatedclasses.Sequences; import org.jooq.test.firebird.generatedclasses.tables.records.TAuthorRecord; import org.jooq.test.firebird.generatedclasses.tables.records.TBookRecord; @@ -482,6 +484,16 @@ public class FirebirdTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/H2Test.java b/jOOQ-test/src/org/jooq/test/H2Test.java index ab022e636d..d4e5409f89 100644 --- a/jOOQ-test/src/org/jooq/test/H2Test.java +++ b/jOOQ-test/src/org/jooq/test/H2Test.java @@ -53,6 +53,7 @@ import org.jooq.ArrayRecord; import org.jooq.DAO; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -69,6 +70,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.h2.generatedclasses.Keys; import org.jooq.test.h2.generatedclasses.Routines; import org.jooq.test.h2.generatedclasses.Sequences; import org.jooq.test.h2.generatedclasses.tables.TArrays; @@ -514,6 +516,16 @@ public class H2Test extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return VLibrary.V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/HSQLDBTest.java b/jOOQ-test/src/org/jooq/test/HSQLDBTest.java index 0dfe71f47d..8ad42017fc 100644 --- a/jOOQ-test/src/org/jooq/test/HSQLDBTest.java +++ b/jOOQ-test/src/org/jooq/test/HSQLDBTest.java @@ -61,6 +61,7 @@ import java.sql.Timestamp; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -77,6 +78,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.hsqldb.generatedclasses.Keys; import org.jooq.test.hsqldb.generatedclasses.Routines; import org.jooq.test.hsqldb.generatedclasses.Sequences; import org.jooq.test.hsqldb.generatedclasses.tables.records.TArraysRecord; @@ -498,6 +500,16 @@ public class HSQLDBTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/HSQLDBTest2.java b/jOOQ-test/src/org/jooq/test/HSQLDBTest2.java index 4532d6d321..e03f522a52 100644 --- a/jOOQ-test/src/org/jooq/test/HSQLDBTest2.java +++ b/jOOQ-test/src/org/jooq/test/HSQLDBTest2.java @@ -58,6 +58,7 @@ import java.util.List; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -77,6 +78,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.h2.generatedclasses.Keys; import org.jooq.test.h2.generatedclasses.Routines; import org.jooq.test.h2.generatedclasses.Sequences; import org.jooq.test.h2.generatedclasses.tables.records.TArraysRecord; @@ -502,6 +504,16 @@ public class HSQLDBTest2 extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/IngresTest.java b/jOOQ-test/src/org/jooq/test/IngresTest.java index 7fac37b380..b2bbd23a8a 100644 --- a/jOOQ-test/src/org/jooq/test/IngresTest.java +++ b/jOOQ-test/src/org/jooq/test/IngresTest.java @@ -51,6 +51,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -67,6 +68,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.ingres.generatedclasses.Keys; import org.jooq.test.ingres.generatedclasses.Sequences; import org.jooq.test.ingres.generatedclasses.tables.TAuthor; import org.jooq.test.ingres.generatedclasses.tables.TBook; @@ -489,6 +491,16 @@ public class IngresTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return VLibrary.V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/MySQLTest.java b/jOOQ-test/src/org/jooq/test/MySQLTest.java index 416f50b579..84a827d0cd 100644 --- a/jOOQ-test/src/org/jooq/test/MySQLTest.java +++ b/jOOQ-test/src/org/jooq/test/MySQLTest.java @@ -37,6 +37,7 @@ package org.jooq.test; import static junit.framework.Assert.assertNull; +import static org.jooq.impl.Factory.md5; import static org.jooq.impl.Factory.val; import static org.jooq.test.mysql.generatedclasses.Tables.T_BOOK_TO_BOOK_STORE; import static org.jooq.test.mysql.generatedclasses.Tables.T_BOOLEANS; @@ -52,7 +53,6 @@ import static org.jooq.util.mysql.MySQLFactory.decode; import static org.jooq.util.mysql.MySQLFactory.desDecrypt; import static org.jooq.util.mysql.MySQLFactory.desEncrypt; import static org.jooq.util.mysql.MySQLFactory.encode; -import static org.jooq.util.mysql.MySQLFactory.md5; import static org.jooq.util.mysql.MySQLFactory.password; import static org.jooq.util.mysql.MySQLFactory.sha1; import static org.jooq.util.mysql.MySQLFactory.sha2; @@ -70,6 +70,7 @@ import org.jooq.ArrayRecord; import org.jooq.DAO; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -86,6 +87,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.mysql.generatedclasses.Keys; import org.jooq.test.mysql.generatedclasses.Routines; import org.jooq.test.mysql.generatedclasses.enums.TBookStatus; import org.jooq.test.mysql.generatedclasses.enums.T_959JavaKeywords; @@ -520,6 +522,16 @@ public class MySQLTest extends jOOQAbstractTest< return TBook.STATUS; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return VLibrary.V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/MySQLTestSchemaRewrite.java b/jOOQ-test/src/org/jooq/test/MySQLTestSchemaRewrite.java index fe03e0b3ff..92a6a07c52 100644 --- a/jOOQ-test/src/org/jooq/test/MySQLTestSchemaRewrite.java +++ b/jOOQ-test/src/org/jooq/test/MySQLTestSchemaRewrite.java @@ -59,6 +59,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -75,6 +76,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.mysql2.generatedclasses.Keys; import org.jooq.test.mysql2.generatedclasses.Routines; import org.jooq.test.mysql2.generatedclasses.tables.records.TAuthorRecord; import org.jooq.test.mysql2.generatedclasses.tables.records.TBookRecord; @@ -490,6 +492,16 @@ public class MySQLTestSchemaRewrite extends jOOQAbstractTest< return T_BOOK.STATUS; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/OracleTest.java b/jOOQ-test/src/org/jooq/test/OracleTest.java index a70618ed51..08ac8dfe59 100644 --- a/jOOQ-test/src/org/jooq/test/OracleTest.java +++ b/jOOQ-test/src/org/jooq/test/OracleTest.java @@ -90,6 +90,7 @@ import org.jooq.ArrayRecord; import org.jooq.DAO; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -107,6 +108,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; import org.jooq.test.oracle.generatedclasses.multi_schema.tables.records.TBookSaleRecord; +import org.jooq.test.oracle.generatedclasses.test.Keys; import org.jooq.test.oracle.generatedclasses.test.Routines; import org.jooq.test.oracle.generatedclasses.test.Sequences; import org.jooq.test.oracle.generatedclasses.test.packages.Library; @@ -571,6 +573,16 @@ public class OracleTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/PostgresTest.java b/jOOQ-test/src/org/jooq/test/PostgresTest.java index 4261c87f79..851976105b 100644 --- a/jOOQ-test/src/org/jooq/test/PostgresTest.java +++ b/jOOQ-test/src/org/jooq/test/PostgresTest.java @@ -63,6 +63,7 @@ import java.util.List; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -79,6 +80,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.postgres.generatedclasses.Keys; import org.jooq.test.postgres.generatedclasses.Routines; import org.jooq.test.postgres.generatedclasses.Sequences; import org.jooq.test.postgres.generatedclasses.enums.U_959; @@ -496,6 +498,16 @@ public class PostgresTest extends jOOQAbstractTest< return T_BOOK.STATUS; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/SQLServerTest.java b/jOOQ-test/src/org/jooq/test/SQLServerTest.java index 7d5de7ab9e..0d0fe1adbc 100644 --- a/jOOQ-test/src/org/jooq/test/SQLServerTest.java +++ b/jOOQ-test/src/org/jooq/test/SQLServerTest.java @@ -59,6 +59,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -75,6 +76,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.sqlserver.generatedclasses.Keys; import org.jooq.test.sqlserver.generatedclasses.Routines; import org.jooq.test.sqlserver.generatedclasses.tables.records.TAuthorRecord; import org.jooq.test.sqlserver.generatedclasses.tables.records.TBookRecord; @@ -484,6 +486,16 @@ public class SQLServerTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/SQLiteTest.java b/jOOQ-test/src/org/jooq/test/SQLiteTest.java index e205cccf42..1f18aa2dea 100644 --- a/jOOQ-test/src/org/jooq/test/SQLiteTest.java +++ b/jOOQ-test/src/org/jooq/test/SQLiteTest.java @@ -49,6 +49,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -65,6 +66,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.sqlite.generatedclasses.Keys; import org.jooq.test.sqlite.generatedclasses.tables.TAuthor; import org.jooq.test.sqlite.generatedclasses.tables.TBook; import org.jooq.test.sqlite.generatedclasses.tables.TBookStore; @@ -483,6 +485,16 @@ public class SQLiteTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.FK_T_BOOK_T_AUTHOR_1; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.FK_T_BOOK_T_AUTHOR_2; + } + @Override protected Table VLibrary() { return VLibrary.V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/SybaseTest.java b/jOOQ-test/src/org/jooq/test/SybaseTest.java index 352f051bc5..c22d0ad09a 100644 --- a/jOOQ-test/src/org/jooq/test/SybaseTest.java +++ b/jOOQ-test/src/org/jooq/test/SybaseTest.java @@ -54,6 +54,7 @@ import java.sql.Date; import org.jooq.ArrayRecord; import org.jooq.DataType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; @@ -70,6 +71,7 @@ import org.jooq.test._.converters.Boolean_YES_NO_LC; import org.jooq.test._.converters.Boolean_YES_NO_UC; import org.jooq.test._.converters.Boolean_YN_LC; import org.jooq.test._.converters.Boolean_YN_UC; +import org.jooq.test.sybase.generatedclasses.Keys; import org.jooq.test.sybase.generatedclasses.Routines; import org.jooq.test.sybase.generatedclasses.Sequences; import org.jooq.test.sybase.generatedclasses.tables.records.TAuthorRecord; @@ -479,6 +481,16 @@ public class SybaseTest extends jOOQAbstractTest< return null; } + @Override + protected ForeignKey FK_T_BOOK_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_AUTHOR_ID; + } + + @Override + protected ForeignKey FK_T_BOOK_CO_AUTHOR_ID() { + return Keys.T_BOOK__FK_T_BOOK_CO_AUTHOR_ID; + } + @Override protected Table VLibrary() { return V_LIBRARY; diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/ReferentialTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/ReferentialTests.java new file mode 100644 index 0000000000..ccbfad4157 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/_/testcases/ReferentialTests.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.test._.testcases; + +import static java.util.Arrays.asList; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; + +import java.sql.Date; + +import org.jooq.Record1; +import org.jooq.Record2; +import org.jooq.Record3; +import org.jooq.Record6; +import org.jooq.Result; +import org.jooq.TableRecord; +import org.jooq.UpdatableRecord; +import org.jooq.test.BaseTest; +import org.jooq.test.jOOQAbstractTest; + +import org.junit.Test; + +public class ReferentialTests< + A extends UpdatableRecord & Record6, + AP, + B extends UpdatableRecord, + S extends UpdatableRecord & Record1, + B2S extends UpdatableRecord & Record3, + BS extends UpdatableRecord, + L extends TableRecord & Record2, + X extends TableRecord, + DATE extends UpdatableRecord, + BOOL extends UpdatableRecord, + D extends UpdatableRecord, + T extends UpdatableRecord, + U extends TableRecord, + I extends TableRecord, + IPK extends UpdatableRecord, + T725 extends UpdatableRecord, + T639 extends UpdatableRecord, + T785 extends TableRecord> +extends BaseTest { + + public ReferentialTests(jOOQAbstractTest delegate) { + super(delegate); + } + + @SuppressWarnings("unchecked") + @Test + public void testFetchParentAndChildren() throws Exception { + Result authors = create().selectFrom(TAuthor()).orderBy(TAuthor_ID()).fetch(); + Result books = create().selectFrom(TBook()).orderBy(TBook_ID()).fetch(); + + A a1 = authors.get(0); + A a2 = authors.get(1); + B b1 = books.get(0); + B b2 = books.get(1); + B b3 = books.get(2); + B b4 = books.get(3); + + // Fetching parents + assertEquals(a1, b1.fetchParent(FK_T_BOOK_AUTHOR_ID())); + assertEquals(a1, FK_T_BOOK_AUTHOR_ID().fetchParent(b1)); + assertSame(asList(a1), FK_T_BOOK_AUTHOR_ID().fetchParents(b1, b2)); + assertSame(asList(a1, a2), FK_T_BOOK_AUTHOR_ID().fetchParents(b1, b3)); + assertSame(asList(a1, a2), FK_T_BOOK_AUTHOR_ID().fetchParents(b1, b2, b3, b4)); + + // Fetching children + assertSame(asList(b1, b2), a1.fetchChildren(FK_T_BOOK_AUTHOR_ID())); + assertSame(asList(b1, b2), FK_T_BOOK_AUTHOR_ID().fetchChildren(a1)); + assertSame(asList(b3, b4), a2.fetchChildren(FK_T_BOOK_AUTHOR_ID())); + assertSame(asList(b3, b4), FK_T_BOOK_AUTHOR_ID().fetchChildren(a2)); + assertSame(asList(b1, b2, b3, b4), FK_T_BOOK_AUTHOR_ID().fetchChildren(a1, a2)); + + // No co-authors available + assertNull(b1.fetchParent(FK_T_BOOK_CO_AUTHOR_ID())); + assertNull(a1.fetchChild(FK_T_BOOK_CO_AUTHOR_ID())); + } +} diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index ca939b8285..febceb7382 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -65,6 +65,7 @@ import org.jooq.DAO; import org.jooq.DataType; import org.jooq.ExecuteType; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Record; import org.jooq.Record1; import org.jooq.Record2; @@ -117,6 +118,7 @@ import org.jooq.test._.testcases.OrderByTests; import org.jooq.test._.testcases.PlainSQLTests; import org.jooq.test._.testcases.PredicateTests; import org.jooq.test._.testcases.RecordTests; +import org.jooq.test._.testcases.ReferentialTests; import org.jooq.test._.testcases.RenderAndBindTests; import org.jooq.test._.testcases.ResultTests; import org.jooq.test._.testcases.RoutineAndUDTTests; @@ -656,6 +658,8 @@ public abstract class jOOQAbstractTest< protected TableField TBook_REC_TIMESTAMP() { return null; } + protected abstract ForeignKey FK_T_BOOK_AUTHOR_ID(); + protected abstract ForeignKey FK_T_BOOK_CO_AUTHOR_ID(); protected abstract UpdatableTable TBookStore(); protected abstract TableField TBookStore_NAME(); @@ -1088,6 +1092,11 @@ public abstract class jOOQAbstractTest< new ResultTests(this).testResultSort(); } + @Test + public void testFetchParentAndChildren() throws Exception { + new ReferentialTests(this).testFetchParentAndChildren(); + } + @Test public void testFetch() throws Exception { new FetchTests(this).testFetch(); diff --git a/jOOQ/src/main/java/org/jooq/ForeignKey.java b/jOOQ/src/main/java/org/jooq/ForeignKey.java index fc057bacfd..ad5fe85f00 100644 --- a/jOOQ/src/main/java/org/jooq/ForeignKey.java +++ b/jOOQ/src/main/java/org/jooq/ForeignKey.java @@ -35,18 +35,92 @@ */ package org.jooq; +import java.util.Collection; + +import org.jooq.exception.DataAccessException; + /** * A ForeignKey is an object referencing a {@link UniqueKey}. It * represents a FOREIGN KEY relationship between two tables. - * + * * @param The FOREIGN KEY's owner table record - * @param The referenced KEY's owner table record + * @param The referenced KEY's owner table record * @author Lukas Eder */ -public interface ForeignKey extends Key { +public interface ForeignKey extends Key { /** * The referenced Key */ - UniqueKey getKey(); + UniqueKey getKey(); + + /** + * Fetch a parent record of a given record through this foreign key + *

+ * This returns a parent record referenced by a given record through this + * foreign key. If no parent record was found, this returns + * null + * + * @throws DataAccessException if something went wrong executing the query + * @see TableRecord#fetchParent(ForeignKey) + */ + O fetchParent(R record) throws DataAccessException; + + /** + * Fetch parent records of a given set of record through this foreign key + *

+ * This returns parent records referenced by any record in a given set of + * records through this foreign key. + * + * @throws DataAccessException if something went wrong executing the query + * @see TableRecord#fetchParent(ForeignKey) + */ + Result fetchParents(R... records) throws DataAccessException; + + /** + * Fetch parent records of a given set of record through this foreign key + *

+ * This returns parent records referenced by any record in a given set of + * records through this foreign key. + * + * @throws DataAccessException if something went wrong executing the query + * @see TableRecord#fetchParent(ForeignKey) + */ + Result fetchParents(Collection records) throws DataAccessException; + + /** + * Fetch child records of a given record through this foreign key + *

+ * This returns childs record referencing a given record through this + * foreign key + * + * @throws DataAccessException if something went wrong executing the query + * @see UpdatableRecord#fetchChild(ForeignKey) + * @see UpdatableRecord#fetchChildren(ForeignKey) + */ + Result fetchChildren(O record) throws DataAccessException; + + /** + * Fetch child records of a given set of records through this foreign key + *

+ * This returns childs record referencing any record in a given set of + * records through this foreign key + * + * @throws DataAccessException if something went wrong executing the query + * @see UpdatableRecord#fetchChild(ForeignKey) + * @see UpdatableRecord#fetchChildren(ForeignKey) + */ + Result fetchChildren(O... records) throws DataAccessException; + + /** + * Fetch child records of a given set of records through this foreign key + *

+ * This returns childs record referencing any record in a given set of + * records through this foreign key + * + * @throws DataAccessException if something went wrong executing the query + * @see UpdatableRecord#fetchChild(ForeignKey) + * @see UpdatableRecord#fetchChildren(ForeignKey) + */ + Result fetchChildren(Collection records) throws DataAccessException; } diff --git a/jOOQ/src/main/java/org/jooq/TableRecord.java b/jOOQ/src/main/java/org/jooq/TableRecord.java index 7e933effa0..d796c4dbbc 100644 --- a/jOOQ/src/main/java/org/jooq/TableRecord.java +++ b/jOOQ/src/main/java/org/jooq/TableRecord.java @@ -35,6 +35,7 @@ */ package org.jooq; +import org.jooq.exception.DataAccessException; /** * A record originating from a single table @@ -54,4 +55,18 @@ public interface TableRecord> extends Record { */ @Override R original(); + + /** + * Fetch a parent record of this record, given a foreign key + *

+ * This returns a parent record referenced by this record through a given + * foreign key. If no parent record was found, this returns + * null + * + * @throws DataAccessException if something went wrong executing the query + * @see ForeignKey#fetchParent(Record) + * @see ForeignKey#fetchParents(java.util.Collection) + * @see ForeignKey#fetchParents(Record...) + */ + > O fetchParent(ForeignKey key) throws DataAccessException; } diff --git a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java index f529e214b8..96d341667d 100644 --- a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java +++ b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java @@ -41,6 +41,7 @@ import java.sql.Statement; import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataChangedException; +import org.jooq.exception.InvalidResultException; import org.jooq.impl.Executor; /** @@ -304,7 +305,7 @@ public interface UpdatableRecord> extends Updatable * record does not exist anymore in the database * */ - void refresh(Field... fields); + void refresh(Field... fields) throws DataAccessException; /** * Duplicate this record (in memory) and reset all fields from the primary @@ -314,4 +315,31 @@ public interface UpdatableRecord> extends Updatable * @return A new record, distinct from this record. */ R copy(); + + /** + * Fetch a child record of this record, given a foreign key + *

+ * This returns a child record referencing this record through a given + * foreign key. If no child record was found, this returns null + * + * @throws DataAccessException if something went wrong executing the query + * @throws InvalidResultException if the query returned more than one record + * @see ForeignKey#fetchChildren(java.util.Collection) + * @see ForeignKey#fetchChildren(Record) + * @see ForeignKey#fetchChildren(Record...) + */ + > O fetchChild(ForeignKey key) throws InvalidResultException, DataAccessException; + + /** + * Fetch child records of this record, given a foreign key + *

+ * This returns childs record referencing this record through a given + * foreign key. + * + * @throws DataAccessException if something went wrong executing the query + * @see ForeignKey#fetchChildren(java.util.Collection) + * @see ForeignKey#fetchChildren(Record) + * @see ForeignKey#fetchChildren(Record...) + */ + > Result fetchChildren(ForeignKey key) throws DataAccessException; } diff --git a/jOOQ/src/main/java/org/jooq/impl/Executor.java b/jOOQ/src/main/java/org/jooq/impl/Executor.java index c59d71a2b3..ccbee14e75 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Executor.java +++ b/jOOQ/src/main/java/org/jooq/impl/Executor.java @@ -1154,7 +1154,7 @@ public class Executor implements Configuration { */ @Support public final Record fetchOne(ResultSet rs) throws DataAccessException, InvalidResultException { - return filterOne(fetchLazy(rs).fetch()); + return Utils.filterOne(fetchLazy(rs).fetch()); } /** @@ -2417,7 +2417,7 @@ public class Executor implements Configuration { */ @Support public final R fetchOne(Table table) throws DataAccessException, InvalidResultException { - return filterOne(fetch(table)); + return Utils.filterOne(fetch(table)); } /** @@ -2434,7 +2434,7 @@ public class Executor implements Configuration { */ @Support public final R fetchOne(Table table, Condition condition) throws DataAccessException, InvalidResultException { - return filterOne(fetch(table, condition)); + return Utils.filterOne(fetch(table, condition)); } /** @@ -2450,7 +2450,7 @@ public class Executor implements Configuration { */ @Support public final R fetchAny(Table table) throws DataAccessException { - return filterOne(selectFrom(table).limit(1).fetch()); + return Utils.filterOne(selectFrom(table).limit(1).fetch()); } /** @@ -2549,19 +2549,6 @@ public class Executor implements Configuration { // XXX Internals // ------------------------------------------------------------------------- - private static R filterOne(List list) throws InvalidResultException { - int size = list.size(); - - if (size == 1) { - return list.get(0); - } - else if (size > 1) { - throw new InvalidResultException("Too many rows selected : " + size); - } - - return null; - } - @Override public String toString() { return configuration.toString(); diff --git a/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java b/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java index a1a8d24b42..d28b6e3cf5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ReferenceImpl.java @@ -35,32 +35,163 @@ */ package org.jooq.impl; +import static org.jooq.impl.Factory.row; +import static org.jooq.impl.Utils.filterOne; +import static org.jooq.impl.Utils.first; +import static org.jooq.impl.Utils.list; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.jooq.AttachableInternal; +import org.jooq.Field; import org.jooq.ForeignKey; import org.jooq.Record; +import org.jooq.Result; +import org.jooq.RowN; import org.jooq.Table; import org.jooq.TableField; import org.jooq.UniqueKey; +import org.jooq.exception.DetachedException; /** * @author Lukas Eder */ -class ReferenceImpl extends AbstractKey implements ForeignKey { +class ReferenceImpl extends AbstractKey implements ForeignKey { /** * Generated UID */ private static final long serialVersionUID = 3636724364192618701L; - private final UniqueKey key; + private final UniqueKey key; - ReferenceImpl(UniqueKey key, Table table, TableField... fields) { + ReferenceImpl(UniqueKey key, Table table, TableField... fields) { super(table, fields); this.key = key; } @Override - public final UniqueKey getKey() { + public final UniqueKey getKey() { return key; } + + @SuppressWarnings("unchecked") + @Override + public final O fetchParent(R record) { + return filterOne(fetchParents(record)); + } + + @Override + public final Result fetchParents(R... records) { + return fetchParents(list(records)); + } + + @SuppressWarnings("unchecked") + @Override + public final Result fetchChildren(O record) { + return fetchChildren(list(record)); + } + + @Override + public final Result fetchChildren(O... records) { + return fetchChildren(list(records)); + } + + @Override + public final Result fetchParents(Collection records) { + if (records == null || records.size() == 0) { + return new ResultImpl(new DefaultConfiguration(), new FieldList(key.getFields())); + } + else { + return fetch(records, key.getTable(), key.getFieldsArray(), getFieldsArray()); + } + } + + @Override + public final Result fetchChildren(Collection records) { + if (records == null || records.size() == 0) { + return new ResultImpl(new DefaultConfiguration(), new FieldList(getFields())); + } + else { + return fetch(records, getTable(), getFieldsArray(), key.getFieldsArray()); + } + } + + /** + * Do the actual fetching + */ + @SuppressWarnings("unchecked") + private static Result fetch( + Collection records, + Table table, + TableField[] fields1, + TableField[] fields2) { + + // Use regular predicates + if (fields1.length == 1) { + return extractExecutor(records) + .selectFrom(table) + .where(((Field) fields1[0]).in(extractValues(records, fields2[0]))) + .fetch(); + } + + // Use row value expressions + else { + return extractExecutor(records) + .selectFrom(table) + .where(row(fields1).in(extractRows(records, fields2))) + .fetch(); + } + + } + + /** + * Extract a list of values from a set of records given some fields + */ + private static List extractValues(Collection records, TableField field2) { + List result = new ArrayList(); + + for (R record : records) { + result.add(record.getValue(field2)); + } + + return result; + } + + /** + * Extract a list of row value expressions from a set of records given some fields + */ + private static List extractRows(Collection records, TableField[] fields) { + List rows = new ArrayList(); + + for (R record : records) { + Object[] values = new Object[fields.length]; + + for (int i = 0; i < fields.length; i++) { + values[i] = record.getValue(fields[i]); + } + + rows.add(row(values)); + } + + return rows; + } + + /** + * Extract a configuration from the first record of a collection of records + */ + private static Executor extractExecutor(Collection records) + throws DetachedException { + R first = first(records); + + if (first instanceof AttachableInternal) { + return new Executor(((AttachableInternal) first).getConfiguration()); + } + else { + throw new DetachedException("Supply at least one attachable record"); + } + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java index 5d37749d33..3d7a80af51 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java @@ -35,8 +35,10 @@ */ package org.jooq.impl; +import org.jooq.ForeignKey; import org.jooq.Table; import org.jooq.TableRecord; +import org.jooq.UpdatableRecord; /** * A record implementation for a record originating from a single table @@ -71,4 +73,10 @@ public class TableRecordImpl> extends AbstractRecord im public final R original() { return (R) super.original(); } + + @SuppressWarnings("unchecked") + @Override + public final > O fetchParent(ForeignKey key) { + return key.fetchParent((R) this); + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java index a4e1230cc2..1cbec29b54 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java @@ -47,14 +47,17 @@ import java.util.List; import org.jooq.Configuration; import org.jooq.DeleteQuery; import org.jooq.Field; +import org.jooq.ForeignKey; import org.jooq.Identity; import org.jooq.InsertQuery; import org.jooq.Record; +import org.jooq.Result; import org.jooq.SQLDialect; import org.jooq.SelectQuery; import org.jooq.SimpleSelectQuery; import org.jooq.StoreQuery; import org.jooq.TableField; +import org.jooq.TableRecord; import org.jooq.UniqueKey; import org.jooq.UpdatableRecord; import org.jooq.UpdatableTable; @@ -93,6 +96,17 @@ public class UpdatableRecordImpl> extends TableReco return result; } + @Override + public final > O fetchChild(ForeignKey key) { + return Utils.filterOne(fetchChildren(key)); + } + + @SuppressWarnings("unchecked") + @Override + public final > Result fetchChildren(ForeignKey key) { + return key.fetchChildren((R) this); + } + @Override public final UpdatableTable getTable() { return (UpdatableTable) super.getTable(); diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index 0f89e34bdd..7c0f3c77f8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -70,6 +70,7 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; @@ -101,6 +102,7 @@ import org.jooq.UDTRecord; import org.jooq.UpdatableRecord; import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; +import org.jooq.exception.InvalidResultException; import org.jooq.tools.Convert; import org.jooq.tools.JooqLogger; import org.jooq.tools.LoggerListener; @@ -158,18 +160,6 @@ final class Utils { */ private static final Pattern JDBC_ESCAPE_PATTERN = Pattern.compile("\\{(fn|d|t|ts)\\b.*"); - // ------------------------------------------------------------------------ - // XXX: General utility methods - // ------------------------------------------------------------------------ - - /** - * Use this rather than {@link Arrays#asList(Object...)} for - * null-safety - */ - static final List list(T... array) { - return array == null ? Collections.emptyList() : Arrays.asList(array); - } - // ------------------------------------------------------------------------ // XXX: Record constructors and related methods // ------------------------------------------------------------------------ @@ -388,6 +378,56 @@ final class Utils { // XXX: General utility methods // ------------------------------------------------------------------------ + /** + * Use this rather than {@link Arrays#asList(Object...)} for + * null-safety + */ + static final List list(T... array) { + return array == null ? Collections.emptyList() : Arrays.asList(array); + } + + /** + * Extract the first item from an iterable or null, if there is + * no such item, or if iterable itself is null + */ + static final T first(Iterable iterable) { + if (iterable == null) { + return null; + } + else { + Iterator iterator = iterable.iterator(); + + if (iterator.hasNext()) { + return iterator.next(); + } + else { + return null; + } + } + } + + /** + * Get the only element from a list or null, or throw an + * exception + * + * @param list The list + * @return The only element from the list or null + * @throws InvalidResultException Thrown if the list contains more than one + * element + */ + static R filterOne(List list) throws InvalidResultException { + int size = list.size(); + + if (size == 1) { + return list.get(0); + } + else if (size > 1) { + throw new InvalidResultException("Too many rows selected : " + size); + } + + return null; + } + /** * Render and bind a list of {@link QueryPart} to plain SQL *