[#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:
parent
31bd0d6ae1
commit
1119c72bf6
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user