[#578] Add KEY JOIN syntax to simulate joining using generated foreign keys - Added methods to the Table API (not yet Select API)

This commit is contained in:
Lukas Eder 2012-01-15 22:23:27 +00:00
parent 31bd0d6ae1
commit 1119c72bf6
5 changed files with 238 additions and 0 deletions

View File

@ -234,6 +234,7 @@ public abstract class jOOQAbstractTest<
protected static final List<Short> BOOK_IDS_SHORT = Arrays.asList((short) 1, (short) 2, (short) 3, (short) 4);
protected static final List<Integer> BOOK_IDS = Arrays.asList(1, 2, 3, 4);
protected static final List<Integer> BOOK_AUTHOR_IDS = Arrays.asList(1, 1, 2, 2);
protected static final List<String> BOOK_TITLES = Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida");
protected static final List<String> BOOK_FIRST_NAMES = Arrays.asList("George", "George", "Paulo", "Paulo");
protected static final List<String> BOOK_LAST_NAMES = Arrays.asList("Orwell", "Orwell", "Coelho", "Coelho");
@ -5902,6 +5903,62 @@ public abstract class jOOQAbstractTest<
assertNull(result.getValue(3, TAuthor_LAST_NAME()));
}
@Test
public void testJoinOnKey() throws Exception {
if (!supportsReferences()) {
log.info("SKIPPING", "JOIN ON KEY tests");
return;
}
try {
create().select(TAuthor_ID(), TBook_TITLE())
.from(TAuthor().join(TBook()).onKey());
fail();
}
catch (DataAccessException expected) {}
try {
create().select(TAuthor_ID(), TBook_TITLE())
.from(TAuthor().join(TBook()).onKey(TBook_TITLE()));
fail();
}
catch (DataAccessException expected) {}
try {
create().select(TAuthor_ID(), TBook_TITLE())
.from(TAuthor().join(TBook()).onKey(TBook_AUTHOR_ID(), TBook_ID()));
fail();
}
catch (DataAccessException expected) {}
// Test the Table API
// ------------------
Result<Record> result1 =
create().select(TAuthor_ID(), TBook_TITLE())
.from(TAuthor().join(TBook()).onKey(TBook_AUTHOR_ID()))
.orderBy(TBook_ID())
.fetch();
assertEquals(4, result1.size());
assertEquals(BOOK_AUTHOR_IDS, result1.getValues(0));
assertEquals(BOOK_TITLES, result1.getValues(1));
Result<Record> result2 =
create().select(TAuthor_ID(), TBook_TITLE())
.from(TAuthor()
.join(TBook())
.onKey(TBook_AUTHOR_ID())
.and(TBook_ID().in(1, 2)))
.orderBy(TBook_ID())
.fetch();
// Test the Select API
// -------------------
assertEquals(2, result2.size());
assertEquals(BOOK_AUTHOR_IDS.subList(0, 2), result2.getValues(0));
assertEquals(BOOK_TITLES.subList(0, 2), result2.getValues(1));
}
@Test
public void testOuterJoin() throws Exception {
// Test LEFT OUTER JOIN

View File

@ -123,6 +123,43 @@ public interface SelectOnStep {
@Support
SelectOnConditionStep on(String sql, Object... bindings);
// /**
// * Join the previous table on a non-ambiguous foreign key relationship
// * between the two joined tables.
// * <p>
// * See {@link Table#onKey(Key)} for examples.
// *
// * @see Table#onKey(Key)
// * @throws DataAccessException If there is no non-ambiguous key definition
// * known to jOOQ
// */
// @Support
// SelectOnConditionStep onKey() throws DataAccessException;
//
// /**
// * Join the previous table on a non-ambiguous foreign key relationship
// * between the two joined tables.
// * <p>
// * See {@link Table#onKey(Key)} for examples.
// *
// * @see Table#onKey(Key)
// * @throws DataAccessException If there is no non-ambiguous key definition
// * known to jOOQ
// */
// @Support
// SelectOnConditionStep onKey(TableField<?, ?>... keyFields) throws DataAccessException;
//
// /**
// * Join the table on a non-ambiguous foreign key relationship between the
// * two joined tables.
// * <p>
// * See {@link Table#onKey(Key)} for examples.
// *
// * @see Table#onKey(Key)
// */
// @Support
// SelectOnConditionStep onKey(ForeignKey<?, ?> key);
/**
* Join the previous table with the <code>USING(column [, column...])</code>
* syntax

View File

@ -129,6 +129,42 @@ public interface SelectQuery extends Select<Record>, ConditionProvider, OrderPro
@Support({ DERBY, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLITE })
void addJoinUsing(TableLike<?> table, JoinType type, Collection<? extends Field<?>> fields);
// /**
// * Joins the existing table product to a new table using a foreign key
// *
// * @param table The joined table
// * @param type The type of join
// * @see Table#onKey(Key)
// * @throws DataAccessException If there is no non-ambiguous key definition
// * known to jOOQ
// */
// @Support
// void addJoinOnKey(TableLike<?> table, JoinType type) throws DataAccessException;
//
// /**
// * Joins the existing table product to a new table using a foreign key
// *
// * @param table The joined table
// * @param type The type of join
// * @param keyFields The foreign key fields
// * @see Table#onKey(Key)
// * @throws DataAccessException If there is no non-ambiguous key definition
// * known to jOOQ
// */
// @Support
// void addJoinOnKey(TableLike<?> table, JoinType type, TableField<?, ?>... keyFields) throws DataAccessException;
//
// /**
// * Joins the existing table product to a new table using a foreign key
// *
// * @param table The joined table
// * @param type The type of join
// * @param key The foreign key
// * @see Table#onKey(Key)
// */
// @Support
// void addJoinOnKey(TableLike<?> table, JoinType type, ForeignKey<?, ?> key);
/**
* Adds grouping fields
*

View File

@ -45,6 +45,7 @@ import static org.jooq.SQLDialect.SQLITE;
import java.util.Collection;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.Factory;
/**
@ -106,4 +107,53 @@ public interface TableOnStep {
*/
@Support({ DERBY, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLITE })
Table<Record> using(Collection<? extends Field<?>> fields);
/**
* Join the table on a non-ambiguous foreign key relationship between the
* two joined tables.
* <p>
* See {@link #onKey(Key)} for examples.
*
* @see #onKey(Key)
* @throws DataAccessException If there is no non-ambiguous key definition
* known to jOOQ
*/
@Support
TableOnConditionStep onKey() throws DataAccessException;
/**
* Join the table on a non-ambiguous foreign key relationship between the
* two joined tables.
* <p>
* See {@link #onKey(Key)} for examples.
*
* @see #onKey(Key)
* @throws DataAccessException If there is no non-ambiguous key definition
* known to jOOQ
*/
@Support
TableOnConditionStep onKey(TableField<?, ?>... keyFields) throws DataAccessException;
/**
* Join the table on a non-ambiguous foreign key relationship between the
* two joined tables.
* <p>
* An example: <code><pre>
* // There is a single foreign key relationship between A and B and it can
* // be obtained by A.getReferencesTo(B) or vice versa. The order of A and
* // B is not important
* A.join(B).onKey();
*
* // There are several foreign key relationships between A and B. In order
* // to disambiguate, you can provide a formal org.jooq.Key reference from
* // the generated Keys class
* A.join(B).onKey(key);
*
* // There are several foreign key relationships between A and B. In order
* // to disambiguate, you can provide any non-ambiguous foreign key column
* A.join(B).onKey(B.A_ID);
* </pre></code>
*/
@Support
TableOnConditionStep onKey(ForeignKey<?, ?> key);
}

View File

@ -47,6 +47,7 @@ import org.jooq.Attachable;
import org.jooq.BindContext;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.JoinType;
import org.jooq.Operator;
import org.jooq.QueryPart;
@ -55,6 +56,7 @@ import org.jooq.RenderContext;
import org.jooq.SQLDialect;
import org.jooq.Select;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableLike;
import org.jooq.TableOnConditionStep;
import org.jooq.TableOnStep;
@ -205,6 +207,62 @@ class JoinTable extends AbstractTable<Record> implements TableOnStep, TableOnCon
return this;
}
@Override
public final JoinTable onKey() throws DataAccessException {
List<?> leftToRight = lhs.getReferencesTo(rhs);
List<?> rightToLeft = rhs.getReferencesTo(lhs);
if (leftToRight.size() == 1 && rightToLeft.size() == 0) {
return onKey((ForeignKey<?, ?>) leftToRight.get(0));
}
else if (rightToLeft.size() == 1 && leftToRight.size() == 0) {
return onKey((ForeignKey<?, ?>) rightToLeft.get(0));
}
throw onKeyException();
}
@Override
public final JoinTable onKey(TableField<?, ?>... keyFields) throws DataAccessException {
if (keyFields != null && keyFields.length > 0) {
if (keyFields[0].getTable().equals(lhs)) {
for (ForeignKey<?, ?> key : lhs.getReferences()) {
if (key.getFields().containsAll(asList(keyFields))) {
return onKey(key);
}
}
}
else if (keyFields[0].getTable().equals(rhs)) {
for (ForeignKey<?, ?> key : rhs.getReferences()) {
if (key.getFields().containsAll(asList(keyFields))) {
return onKey(key);
}
}
}
}
throw onKeyException();
}
@SuppressWarnings("unchecked")
@Override
public final JoinTable onKey(ForeignKey<?, ?> key) {
JoinTable result = this;
TableField<?, ?>[] references = key.getFieldsArray();
TableField<?, ?>[] referenced = key.getKey().getFieldsArray();
for (int i = 0; i < references.length; i++) {
result.and(((Field<Void>) references[i]).equal((Field<Void>) referenced[i]));
}
return result;
}
private final DataAccessException onKeyException() {
return new DataAccessException("Key ambiguous between tables " + lhs + " and " + rhs);
}
@Override
public final JoinTable using(Field<?>... fields) {
return using(asList(fields));