You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2013/07/22 09:39:07 UTC

git commit: Improved fluent Query builder API by adding string parameter based methods for joining

Updated Branches:
  refs/heads/master c0c5b994e -> d0b4a5d20


Improved fluent Query builder API by adding string parameter based
methods for joining

Project: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/commit/d0b4a5d2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/tree/d0b4a5d2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/diff/d0b4a5d2

Branch: refs/heads/master
Commit: d0b4a5d208f59147c98776f1e38c7bbcf248f2ff
Parents: c0c5b99
Author: kaspers <ka...@kaspers-think.humaninference.com>
Authored: Thu Jul 18 11:22:35 2013 +0200
Committer: kaspers <ka...@kaspers-think.humaninference.com>
Committed: Thu Jul 18 11:22:35 2013 +0200

----------------------------------------------------------------------
 .../query/builder/JoinFromBuilder.java          |  52 +++---
 .../query/builder/JoinFromBuilderImpl.java      | 158 ++++++++--------
 .../query/builder/TableFromBuilder.java         |  70 +++----
 .../query/builder/TableFromBuilderImpl.java     | 185 +++++++++++--------
 .../org/eobjects/metamodel/pojo/Person.java     |  50 +++++
 .../metamodel/pojo/PojoDataContextTest.java     |  35 +++-
 6 files changed, 337 insertions(+), 213 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/d0b4a5d2/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilder.java b/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilder.java
index 1cad5fc..cb57771 100644
--- a/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilder.java
+++ b/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilder.java
@@ -1,26 +1,28 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.eobjects.metamodel.query.builder;
-
-import org.eobjects.metamodel.schema.Column;
-
-public interface JoinFromBuilder extends SatisfiedFromBuilder {
-
-	public SatisfiedFromBuilder on(Column left, Column right);
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eobjects.metamodel.query.builder;
+
+import org.eobjects.metamodel.schema.Column;
+
+public interface JoinFromBuilder extends SatisfiedFromBuilder {
+
+    public SatisfiedFromBuilder on(Column left, Column right) throws IllegalArgumentException;
+
+    public SatisfiedFromBuilder on(String left, String right) throws IllegalArgumentException;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/d0b4a5d2/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilderImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilderImpl.java b/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilderImpl.java
index 6e9520c..656862a 100644
--- a/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilderImpl.java
+++ b/core/src/main/java/org/eobjects/metamodel/query/builder/JoinFromBuilderImpl.java
@@ -1,73 +1,87 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.eobjects.metamodel.query.builder;
-
-import java.util.List;
-
-import org.eobjects.metamodel.DataContext;
-import org.eobjects.metamodel.query.FromItem;
-import org.eobjects.metamodel.query.JoinType;
-import org.eobjects.metamodel.query.Query;
-import org.eobjects.metamodel.query.SelectItem;
-import org.eobjects.metamodel.schema.Column;
-import org.eobjects.metamodel.schema.Table;
-
-final class JoinFromBuilderImpl extends SatisfiedFromBuilderCallback implements
-		JoinFromBuilder {
-
-	private JoinType joinType;
-	private FromItem leftItem;
-	private FromItem rightItem;
-
-	public JoinFromBuilderImpl(Query query, FromItem leftItem,
-			Table rightTable, JoinType joinType, DataContext dataContext) {
-		super(query, dataContext);
-		this.joinType = joinType;
-		this.leftItem = leftItem;
-		this.rightItem = new FromItem(rightTable);
-	}
-
-	@Override
-	public SatisfiedFromBuilder on(Column left, Column right) {
-		if (left == null) {
-			throw new IllegalArgumentException("left cannot be null");
-		}
-		if (right == null) {
-			throw new IllegalArgumentException("right cannot be null");
-		}
-		getQuery().getFromClause().removeItem(leftItem);
-
-		SelectItem[] leftOn = new SelectItem[] { new SelectItem(left) };
-		SelectItem[] rightOn = new SelectItem[] { new SelectItem(right) };
-		FromItem fromItem = new FromItem(joinType, leftItem, rightItem, leftOn,
-				rightOn);
-
-		getQuery().from(fromItem);
-
-		return this;
-	}
-	
-	@Override
-	protected void decorateIdentity(List<Object> identifiers) {
-		super.decorateIdentity(identifiers);
-		identifiers.add(joinType);
-		identifiers.add(leftItem);
-		identifiers.add(rightItem);
-	}
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eobjects.metamodel.query.builder;
+
+import java.util.List;
+
+import org.eobjects.metamodel.DataContext;
+import org.eobjects.metamodel.query.FromItem;
+import org.eobjects.metamodel.query.JoinType;
+import org.eobjects.metamodel.query.Query;
+import org.eobjects.metamodel.query.SelectItem;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.Table;
+
+final class JoinFromBuilderImpl extends SatisfiedFromBuilderCallback implements JoinFromBuilder {
+
+    private JoinType joinType;
+    private FromItem leftItem;
+    private FromItem rightItem;
+
+    public JoinFromBuilderImpl(Query query, FromItem leftItem, Table rightTable, JoinType joinType,
+            DataContext dataContext) {
+        super(query, dataContext);
+        this.joinType = joinType;
+        this.leftItem = leftItem;
+        this.rightItem = new FromItem(rightTable);
+    }
+
+    @Override
+    public SatisfiedFromBuilder on(String left, String right) throws IllegalArgumentException {
+        Table leftTable = leftItem.getTable();
+        if (leftTable == null) {
+            throw new IllegalArgumentException("Left side of join is not a Table, cannot resolve ON item: '" + left + "'.");
+        }
+        Table rightTable = rightItem.getTable();
+        if (rightTable == null) {
+            throw new IllegalArgumentException("Right side of join is not a Table, cannot resolve ON item: '" + right + "'.");
+        }
+
+        Column leftColumn = leftTable.getColumnByName(left);
+        Column rightColumn = rightTable.getColumnByName(right);
+        return on(leftColumn, rightColumn);
+    }
+
+    @Override
+    public SatisfiedFromBuilder on(Column left, Column right) throws IllegalArgumentException {
+        if (left == null) {
+            throw new IllegalArgumentException("left cannot be null");
+        }
+        if (right == null) {
+            throw new IllegalArgumentException("right cannot be null");
+        }
+        getQuery().getFromClause().removeItem(leftItem);
+
+        SelectItem[] leftOn = new SelectItem[] { new SelectItem(left) };
+        SelectItem[] rightOn = new SelectItem[] { new SelectItem(right) };
+        FromItem fromItem = new FromItem(joinType, leftItem, rightItem, leftOn, rightOn);
+
+        getQuery().from(fromItem);
+
+        return this;
+    }
+
+    @Override
+    protected void decorateIdentity(List<Object> identifiers) {
+        super.decorateIdentity(identifiers);
+        identifiers.add(joinType);
+        identifiers.add(leftItem);
+        identifiers.add(rightItem);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/d0b4a5d2/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilder.java b/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilder.java
index d030653..c0254bf 100644
--- a/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilder.java
+++ b/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilder.java
@@ -1,33 +1,39 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.eobjects.metamodel.query.builder;
-
-import org.eobjects.metamodel.schema.Table;
-
-public interface TableFromBuilder extends
-		SatisfiedFromBuilder {
-
-	public JoinFromBuilder innerJoin(Table table);
-	
-	public JoinFromBuilder leftJoin(Table table);
-	
-	public JoinFromBuilder rightJoin(Table table);
-
-	public TableFromBuilder as(String alias);
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eobjects.metamodel.query.builder;
+
+import org.eobjects.metamodel.schema.Table;
+
+public interface TableFromBuilder extends
+		SatisfiedFromBuilder {
+
+	public JoinFromBuilder innerJoin(Table table);
+	
+	public JoinFromBuilder innerJoin(String tableName);
+	
+	public JoinFromBuilder leftJoin(Table table);
+	
+	public JoinFromBuilder leftJoin(String tableName);
+	
+	public JoinFromBuilder rightJoin(Table table);
+	
+	public JoinFromBuilder rightJoin(String tableName);
+
+	public TableFromBuilder as(String alias);
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/d0b4a5d2/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilderImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilderImpl.java b/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilderImpl.java
index 4460d8e..bddd6ff 100644
--- a/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilderImpl.java
+++ b/core/src/main/java/org/eobjects/metamodel/query/builder/TableFromBuilderImpl.java
@@ -1,83 +1,104 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.eobjects.metamodel.query.builder;
-
-import java.util.List;
-
-import org.eobjects.metamodel.DataContext;
-import org.eobjects.metamodel.query.FromItem;
-import org.eobjects.metamodel.query.JoinType;
-import org.eobjects.metamodel.query.Query;
-import org.eobjects.metamodel.schema.Table;
-
-final class TableFromBuilderImpl extends SatisfiedFromBuilderCallback implements
-		TableFromBuilder {
-
-	private FromItem fromItem;
-
-	public TableFromBuilderImpl(Table table, Query query,
-			DataContext dataContext) {
-		super(query, dataContext);
-
-		fromItem = new FromItem(table);
-		query.from(fromItem);
-	}
-
-	@Override
-	public JoinFromBuilder innerJoin(Table table) {
-		if (table == null) {
-			throw new IllegalArgumentException("table cannot be null");
-		}
-		return new JoinFromBuilderImpl(getQuery(), fromItem, table,
-				JoinType.INNER, getDataContext());
-	}
-
-	@Override
-	public JoinFromBuilder leftJoin(Table table) {
-		if (table == null) {
-			throw new IllegalArgumentException("table cannot be null");
-		}
-		return new JoinFromBuilderImpl(getQuery(), fromItem, table,
-				JoinType.LEFT, getDataContext());
-	}
-
-	@Override
-	public JoinFromBuilder rightJoin(Table table) {
-		if (table == null) {
-			throw new IllegalArgumentException("table cannot be null");
-		}
-		return new JoinFromBuilderImpl(getQuery(), fromItem, table,
-				JoinType.RIGHT, getDataContext());
-	}
-
-	@Override
-	public TableFromBuilder as(String alias) {
-		if (alias == null) {
-			throw new IllegalArgumentException("alias cannot be null");
-		}
-		fromItem.setAlias(alias);
-		return this;
-	}
-
-	@Override
-	protected void decorateIdentity(List<Object> identifiers) {
-		super.decorateIdentity(identifiers);
-		identifiers.add(fromItem);
-	}
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eobjects.metamodel.query.builder;
+
+import java.util.List;
+
+import org.eobjects.metamodel.DataContext;
+import org.eobjects.metamodel.query.FromItem;
+import org.eobjects.metamodel.query.JoinType;
+import org.eobjects.metamodel.query.Query;
+import org.eobjects.metamodel.schema.Table;
+
+final class TableFromBuilderImpl extends SatisfiedFromBuilderCallback implements TableFromBuilder {
+
+    private FromItem fromItem;
+
+    public TableFromBuilderImpl(Table table, Query query, DataContext dataContext) {
+        super(query, dataContext);
+
+        fromItem = new FromItem(table);
+        query.from(fromItem);
+    }
+
+    @Override
+    public JoinFromBuilder innerJoin(String tableName) {
+        return innerJoin(findTable(tableName));
+    }
+
+    @Override
+    public JoinFromBuilder innerJoin(Table table) {
+        if (table == null) {
+            throw new IllegalArgumentException("table cannot be null");
+        }
+        return new JoinFromBuilderImpl(getQuery(), fromItem, table, JoinType.INNER, getDataContext());
+    }
+
+    @Override
+    public JoinFromBuilder leftJoin(String tableName) {
+        return leftJoin(findTable(tableName));
+    }
+
+    @Override
+    public JoinFromBuilder leftJoin(Table table) {
+        if (table == null) {
+            throw new IllegalArgumentException("table cannot be null");
+        }
+        return new JoinFromBuilderImpl(getQuery(), fromItem, table, JoinType.LEFT, getDataContext());
+    }
+
+    @Override
+    public JoinFromBuilder rightJoin(String tableName) {
+        return rightJoin(findTable(tableName));
+    }
+
+    @Override
+    public JoinFromBuilder rightJoin(Table table) {
+        if (table == null) {
+            throw new IllegalArgumentException("table cannot be null");
+        }
+        return new JoinFromBuilderImpl(getQuery(), fromItem, table, JoinType.RIGHT, getDataContext());
+    }
+
+    @Override
+    public TableFromBuilder as(String alias) {
+        if (alias == null) {
+            throw new IllegalArgumentException("alias cannot be null");
+        }
+        fromItem.setAlias(alias);
+        return this;
+    }
+
+    @Override
+    protected void decorateIdentity(List<Object> identifiers) {
+        super.decorateIdentity(identifiers);
+        identifiers.add(fromItem);
+    }
+
+    private Table findTable(String tableName) {
+        if (tableName == null) {
+            throw new IllegalArgumentException("tableName cannot be null");
+        }
+        Table table = getDataContext().getTableByQualifiedLabel(tableName);
+        if (table == null) {
+            throw new IllegalArgumentException("No such table: " + tableName);
+        }
+        return table;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/d0b4a5d2/pojo/src/test/java/org/eobjects/metamodel/pojo/Person.java
----------------------------------------------------------------------
diff --git a/pojo/src/test/java/org/eobjects/metamodel/pojo/Person.java b/pojo/src/test/java/org/eobjects/metamodel/pojo/Person.java
new file mode 100644
index 0000000..0e58bc6
--- /dev/null
+++ b/pojo/src/test/java/org/eobjects/metamodel/pojo/Person.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eobjects.metamodel.pojo;
+
+public class Person {
+
+    private String name;
+    private int age;
+    
+    public Person() {
+    }
+    
+    public Person(String name, int age) {
+        this();
+        setName(name);
+        setAge(age);
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/d0b4a5d2/pojo/src/test/java/org/eobjects/metamodel/pojo/PojoDataContextTest.java
----------------------------------------------------------------------
diff --git a/pojo/src/test/java/org/eobjects/metamodel/pojo/PojoDataContextTest.java b/pojo/src/test/java/org/eobjects/metamodel/pojo/PojoDataContextTest.java
index 4bb5a5d..011b395 100644
--- a/pojo/src/test/java/org/eobjects/metamodel/pojo/PojoDataContextTest.java
+++ b/pojo/src/test/java/org/eobjects/metamodel/pojo/PojoDataContextTest.java
@@ -25,6 +25,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import junit.framework.TestCase;
+
+import org.eobjects.metamodel.DataContext;
 import org.eobjects.metamodel.UpdateCallback;
 import org.eobjects.metamodel.UpdateScript;
 import org.eobjects.metamodel.data.DataSet;
@@ -33,10 +36,38 @@ import org.eobjects.metamodel.schema.Schema;
 import org.eobjects.metamodel.schema.Table;
 import org.eobjects.metamodel.util.SimpleTableDef;
 
-import junit.framework.TestCase;
-
 public class PojoDataContextTest extends TestCase {
 
+    public void testExampleScenario() throws Exception {
+        Collection<Person> persons = new ArrayList<Person>();
+        persons.add(new Person("Bono", 42));
+        persons.add(new Person("Elvis Presley", 42));
+
+        Map<String, Object> record1 = new HashMap<String, Object>();
+        record1.put("name", "Bruce Springsteen");
+        record1.put("title", "The Boss");
+
+        Map<String, Object> record2 = new HashMap<String, Object>();
+        record2.put("name", "Elvis Presley");
+        record2.put("title", "The King");
+
+        List<Map<String, ?>> titles = new ArrayList<Map<String, ?>>();
+        titles.add(record1);
+        titles.add(record2);
+
+        TableDataProvider<?> provider1 = new ObjectTableDataProvider<Person>("persons", Person.class, persons);
+        TableDataProvider<?> provider2 = new MapTableDataProvider(new SimpleTableDef("titles", new String[] { "name",
+                "title" }), titles);
+
+        DataContext dc = new PojoDataContext(Arrays.<TableDataProvider<?>> asList(provider1, provider2));
+
+        DataSet dataSet = dc.query().from("persons").innerJoin("titles").on("name", "name").selectAll().execute();
+
+        assertTrue(dataSet.next());
+        assertEquals("Row[values=[Elvis Presley, 42, Elvis Presley, The King]]", dataSet.getRow().toString());
+        assertFalse(dataSet.next());
+    }
+
     public void testScenarioWithMap() throws Exception {
         final SimpleTableDef tableDef = new SimpleTableDef("bar", new String[] { "col1", "col2", "col3" },
                 new ColumnType[] { ColumnType.VARCHAR, ColumnType.INTEGER, ColumnType.BOOLEAN });