From 4adf5a9ebac9496f99a8725bbef8f5470b5877d5 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 17 Dec 2014 23:58:27 +1300 Subject: [PATCH] Fix for #224 - ENH: Add tableAliasMapping() to RawSqlBuilder ... for better mapping of complex objects with raw sql --- src/main/java/com/avaje/ebean/RawSql.java | 36 +++++- .../java/com/avaje/ebean/RawSqlBuilder.java | 14 ++- .../com/avaje/ebean/TestRawSqlBuilder.java | 117 ++++++++++++------ .../tests/rawsql/TestRawSqlMasterDetail.java | 58 +++++++++ 4 files changed, 177 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/avaje/ebean/RawSql.java b/src/main/java/com/avaje/ebean/RawSql.java index 0397a2d1c..c14da59e5 100644 --- a/src/main/java/com/avaje/ebean/RawSql.java +++ b/src/main/java/com/avaje/ebean/RawSql.java @@ -2,12 +2,7 @@ package com.avaje.ebean; import java.io.Serializable; import java.sql.ResultSet; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import com.avaje.ebean.util.CamelCaseHelper; @@ -541,6 +536,22 @@ public final class RawSql implements Serializable { return dbColumnMap.values().iterator(); } + /** + * Modify any column mappings with the given table alias to have the path prefix. + *

+ * For example modify all mappings with table alias "c" to have the path prefix "customer". + *

+ */ + public void tableAliasMapping(String tableAlias, String path) { + + String startMatch = tableAlias+"."; + for (Map.Entry entry : dbColumnMap.entrySet()) { + if (entry.getKey().startsWith(startMatch)) { + entry.getValue().tableAliasMapping(path); + } + } + } + /** * A Column of the RawSql that is mapped to a bean property (or ignored). */ @@ -622,10 +633,23 @@ public final class RawSql implements Serializable { return propertyName; } + /** + * Set the property name mapped to this db column. + */ private void setPropertyName(String propertyName) { this.propertyName = propertyName; } + /** + * Prepend the path to the property name. + *

+ * For example if path is "customer" then "name" becomes "customer.name". + */ + public void tableAliasMapping(String path) { + if (path != null) { + propertyName = path + "." + propertyName; + } + } } } } diff --git a/src/main/java/com/avaje/ebean/RawSqlBuilder.java b/src/main/java/com/avaje/ebean/RawSqlBuilder.java index b491083a7..76418b36f 100644 --- a/src/main/java/com/avaje/ebean/RawSqlBuilder.java +++ b/src/main/java/com/avaje/ebean/RawSqlBuilder.java @@ -108,6 +108,17 @@ public class RawSqlBuilder { return columnMapping(dbColumn, IGNORE_COLUMN); } + /** + * Modify any column mappings with the given table alias to have the path prefix. + *

+ * For example modify all mappings with table alias "c" to have the path prefix "customer". + *

+ */ + public RawSqlBuilder tableAliasMapping(String tableAlias, String path) { + columnMapping.tableAliasMapping(tableAlias, path); + return this; + } + /** * Create the immutable RawSql object. Do this after all the column mapping * has been defined. @@ -122,6 +133,7 @@ public class RawSqlBuilder { protected Sql getSql() { return sql; } - + + } diff --git a/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java b/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java index 3b397f43e..d9217a749 100644 --- a/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java +++ b/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java @@ -5,6 +5,8 @@ import org.junit.Test; import com.avaje.ebean.RawSql.Sql; +import static org.junit.Assert.assertEquals; + public class TestRawSqlBuilder extends BaseTestCase { @Test @@ -12,9 +14,9 @@ public class TestRawSqlBuilder extends BaseTestCase { RawSqlBuilder r = RawSqlBuilder.parse("select id from t_cust"); Sql sql = r.getSql(); - Assert.assertEquals("id", sql.getPreFrom()); - Assert.assertEquals("from t_cust", sql.getPreWhere()); - Assert.assertEquals("", sql.getPreHaving()); + assertEquals("id", sql.getPreFrom()); + assertEquals("from t_cust", sql.getPreWhere()); + assertEquals("", sql.getPreHaving()); Assert.assertNull(sql.getOrderBy()); } @@ -24,9 +26,9 @@ public class TestRawSqlBuilder extends BaseTestCase { RawSqlBuilder r = RawSqlBuilder.parse("select id from t_cust where id > ?"); Sql sql = r.getSql(); - Assert.assertEquals("id", sql.getPreFrom()); - Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); - Assert.assertEquals("", sql.getPreHaving()); + assertEquals("id", sql.getPreFrom()); + assertEquals("from t_cust where id > ?", sql.getPreWhere()); + assertEquals("", sql.getPreHaving()); Assert.assertNull(sql.getOrderBy()); } @@ -35,26 +37,26 @@ public class TestRawSqlBuilder extends BaseTestCase { RawSqlBuilder r = RawSqlBuilder.parse("select id from t_cust where id > ? order by id desc"); Sql sql = r.getSql(); - Assert.assertEquals("id", sql.getPreFrom()); - Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); - Assert.assertEquals("", sql.getPreHaving()); - Assert.assertEquals("order by", sql.getOrderByPrefix()); - Assert.assertEquals("id desc", sql.getOrderBy()); + assertEquals("id", sql.getPreFrom()); + assertEquals("from t_cust where id > ?", sql.getPreWhere()); + assertEquals("", sql.getPreHaving()); + assertEquals("order by", sql.getOrderByPrefix()); + assertEquals("id desc", sql.getOrderBy()); r = RawSqlBuilder.parse("select id from t_cust order by id desc"); sql = r.getSql(); - Assert.assertEquals("id", sql.getPreFrom()); - Assert.assertEquals("from t_cust", sql.getPreWhere()); - Assert.assertEquals("", sql.getPreHaving()); - Assert.assertEquals("id desc", sql.getOrderBy()); + assertEquals("id", sql.getPreFrom()); + assertEquals("from t_cust", sql.getPreWhere()); + assertEquals("", sql.getPreHaving()); + assertEquals("id desc", sql.getOrderBy()); r = RawSqlBuilder .parse("select id, sum(x) from t_cust where id > ? group by id order by id desc"); sql = r.getSql(); - Assert.assertEquals("id, sum(x)", sql.getPreFrom()); - Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); - Assert.assertEquals("group by id", sql.getPreHaving()); - Assert.assertEquals("id desc", sql.getOrderBy()); + assertEquals("id, sum(x)", sql.getPreFrom()); + assertEquals("from t_cust where id > ?", sql.getPreWhere()); + assertEquals("group by id", sql.getPreHaving()); + assertEquals("id desc", sql.getOrderBy()); } @Test @@ -63,40 +65,40 @@ public class TestRawSqlBuilder extends BaseTestCase { RawSqlBuilder r = RawSqlBuilder .parse("select id, sum(x) from t_cust where id > ? group by id having sum(x) > ? order by id desc"); Sql sql = r.getSql(); - Assert.assertEquals("id, sum(x)", sql.getPreFrom()); - Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); - Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); - Assert.assertEquals("order by", sql.getOrderByPrefix()); - Assert.assertEquals("id desc", sql.getOrderBy()); + assertEquals("id, sum(x)", sql.getPreFrom()); + assertEquals("from t_cust where id > ?", sql.getPreWhere()); + assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); + assertEquals("order by", sql.getOrderByPrefix()); + assertEquals("id desc", sql.getOrderBy()); // no where r = RawSqlBuilder .parse("select id, sum(x) from t_cust group by id having sum(x) > ? order by id desc"); sql = r.getSql(); - Assert.assertEquals("id, sum(x)", sql.getPreFrom()); - Assert.assertEquals("from t_cust", sql.getPreWhere()); - Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); - Assert.assertEquals("order by", sql.getOrderByPrefix()); - Assert.assertEquals("id desc", sql.getOrderBy()); + assertEquals("id, sum(x)", sql.getPreFrom()); + assertEquals("from t_cust", sql.getPreWhere()); + assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); + assertEquals("order by", sql.getOrderByPrefix()); + assertEquals("id desc", sql.getOrderBy()); // no where, no order by r = RawSqlBuilder.parse("select id, sum(x) from t_cust group by id having sum(x) > ?"); sql = r.getSql(); - Assert.assertEquals("id, sum(x)", sql.getPreFrom()); - Assert.assertEquals("from t_cust", sql.getPreWhere()); - Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); + assertEquals("id, sum(x)", sql.getPreFrom()); + assertEquals("from t_cust", sql.getPreWhere()); + assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); Assert.assertNull(sql.getOrderBy()); - Assert.assertEquals("order by", sql.getOrderByPrefix()); + assertEquals("order by", sql.getOrderByPrefix()); // no order by r = RawSqlBuilder .parse("select id, sum(x) from t_cust where id > ? group by id having sum(x) > ?"); sql = r.getSql(); - Assert.assertEquals("id, sum(x)", sql.getPreFrom()); - Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); - Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); + assertEquals("id, sum(x)", sql.getPreFrom()); + assertEquals("from t_cust where id > ?", sql.getPreWhere()); + assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); Assert.assertNull(sql.getOrderBy()); - Assert.assertEquals("order by", sql.getOrderByPrefix()); + assertEquals("order by", sql.getOrderByPrefix()); } /** @@ -110,10 +112,43 @@ public class TestRawSqlBuilder extends BaseTestCase { RawSql rawSql = RawSqlBuilder.parse(s).create(); Sql sql = rawSql.getSql(); - Assert.assertEquals("ID, DESCRIPTION, NAME, PARENT_ID", sql.getPreFrom()); - Assert.assertEquals("order siblings by", sql.getOrderByPrefix()); - Assert.assertEquals("NAME", sql.getOrderBy()); - Assert.assertEquals("FROM SOME_TABLE WHERE lower(NAME) like :name START WITH ID = :parentId CONNECT BY PRIOR ID = PARENT_ID", sql.getPreWhere()); + assertEquals("ID, DESCRIPTION, NAME, PARENT_ID", sql.getPreFrom()); + assertEquals("order siblings by", sql.getOrderByPrefix()); + assertEquals("NAME", sql.getOrderBy()); + assertEquals("FROM SOME_TABLE WHERE lower(NAME) like :name START WITH ID = :parentId CONNECT BY PRIOR ID = PARENT_ID", sql.getPreWhere()); } + + + + @Test + public void testWithAlias() { + + String rs = "select o.id, o.status, c.id, c.name, "+ + " d.id, d.order_qty, p.id, p.name " + + "from o_order o join o_customer c on c.id = o.kcustomer_id " + + "join o_order_detail d on d.order_id = o.id " + + "join o_product p on p.id = d.product_id " + + "where o.id <= :maxOrderId and p.id = :productId "+ + "order by o.id, d.id asc"; + + + RawSql rawSql = RawSqlBuilder.parse(rs) + .tableAliasMapping("c", "customer") + .tableAliasMapping("d", "details") + .tableAliasMapping("p", "details.product") + .create(); + + RawSql.ColumnMapping columnMapping = rawSql.getColumnMapping(); + assertEquals(0, columnMapping.getIndexPosition("id")); + assertEquals(1, columnMapping.getIndexPosition("status")); + assertEquals(2, columnMapping.getIndexPosition("customer.id")); + assertEquals(3, columnMapping.getIndexPosition("customer.name")); + assertEquals(4, columnMapping.getIndexPosition("details.id")); + assertEquals(5, columnMapping.getIndexPosition("details.orderQty")); + assertEquals(6, columnMapping.getIndexPosition("details.product.id")); + assertEquals(7, columnMapping.getIndexPosition("details.product.name")); + + } + } diff --git a/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java b/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java index 8e8df67d7..df764e481 100644 --- a/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java +++ b/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java @@ -47,6 +47,64 @@ public class TestRawSqlMasterDetail extends BaseTestCase { } + @Test + public void testWithTableAliasMapping() { + + ResetBasicData.reset(); + + String rs = "select o.id, o.status, c.id, c.name, "+ + " d.id, d.order_qty, p.id, p.name " + + "from o_order o join o_customer c on c.id = o.kcustomer_id " + + "join o_order_detail d on d.order_id = o.id " + + "join o_product p on p.id = d.product_id " + + "where o.id <= :maxOrderId and p.id = :productId "+ + "order by o.id, d.id asc"; + + + RawSql rawSql = RawSqlBuilder.parse(rs) + .tableAliasMapping("c", "customer") + .tableAliasMapping("d", "details") + .tableAliasMapping("p", "details.product") + .create(); + + List ordersFromRaw = Ebean.find(Order.class) + .setRawSql(rawSql) + .setParameter("maxOrderId", 2) + .setParameter("productId", 1) + .findList(); + + printOrders(ordersFromRaw, "using RawSql with tableAlias mapping"); + + } + + + @Test + public void testWithMultipleManys() { + + ResetBasicData.reset(); + + String rs = "select o.id, o.status, c.id, c.name, "+ + " d.id, d.order_qty, p.id, p.name " + + "from o_order o join o_customer c on c.id = o.kcustomer_id " + + "join o_order_detail d on d.order_id = o.id " + + "join o_product p on p.id = d.product_id " + + "order by o.id, d.id asc"; + + + RawSql rawSql = RawSqlBuilder.parse(rs) + .tableAliasMapping("c", "customer") + .tableAliasMapping("d", "details") + .tableAliasMapping("p", "details.product") + .create(); + + List ordersFromRaw = Ebean.find(Order.class) + .setRawSql(rawSql) + .findList(); + + printOrders(ordersFromRaw, "using RawSql with tableAlias mapping"); + + } + private void printOrders(List orders, String heading) { System.out.println("-------------- "+heading); for (Order order : orders) {