diff --git a/src/main/java/com/avaje/ebean/RawSql.java b/src/main/java/com/avaje/ebean/RawSql.java index 320a6f20e..76aac67d9 100644 --- a/src/main/java/com/avaje/ebean/RawSql.java +++ b/src/main/java/com/avaje/ebean/RawSql.java @@ -480,6 +480,13 @@ public final class RawSql implements Serializable { this.queryHashCode = hc; } + /** + * Return true if the property is mapped. + */ + public boolean contains(String property) { + return this.propertyColumnMap.containsKey(property); + } + /** * Creates an immutable copy of this ColumnMapping. * diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java index 1cc52fa52..79e14ef88 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java @@ -265,7 +265,7 @@ public class CQueryBuilder implements Constants { propertyName = SplitName.parent(propertyName); } else if (beanProperty instanceof BeanPropertyAssocOne) { String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]"; - msg += ". It should be mapped to a simple property (proably the Id property). "; + msg += ". It should be mapped to a simple property (probably the Id property). "; throw new PersistenceException(msg); } if (propertyName != null) { @@ -284,8 +284,16 @@ public class CQueryBuilder implements Constants { detail.getChunk(path, true).setDefaultProperties(null, props); } + // check if @Id property included in RawSql + boolean rawNoId = true; + BeanProperty idProperty = descriptor.getIdProperty(); + if (idProperty != null && columnMapping.contains(idProperty.getName())) { + // contains the @Id property for the root level bean + rawNoId = false; + } + // build SqlTree based on OrmQueryDetail of the RawSql - return new SqlTreeBuilder(request, predicates, detail).build(); + return new SqlTreeBuilder(request, predicates, detail, rawNoId).build(); } private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest request, CQueryPredicates predicates, SqlTree select) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java index 0cf0d629a..a5dfc2af7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java @@ -66,15 +66,21 @@ public class SqlTreeBuilder { private final boolean rawSql; + /** + * rawNoId true if the RawSql does not include the @Id property + */ + private final boolean rawNoId; + private SqlTreeNode rootNode; /** * Construct for RawSql query. */ - public SqlTreeBuilder(OrmQueryRequest request, CQueryPredicates predicates, OrmQueryDetail queryDetail) { + public SqlTreeBuilder(OrmQueryRequest request, CQueryPredicates predicates, OrmQueryDetail queryDetail, boolean rawNoId) { this.rawSql = true; this.desc = request.getBeanDescriptor(); + this.rawNoId = rawNoId; this.query = null; this.subQuery = false; this.queryDetail = queryDetail; @@ -95,6 +101,7 @@ public class SqlTreeBuilder { OrmQueryRequest request, CQueryPredicates predicates) { this.rawSql = false; + this.rawNoId = false; this.desc = request.getBeanDescriptor(); this.query = request.getQuery(); @@ -257,7 +264,7 @@ public class SqlTreeBuilder { // Optional many property for lazy loading query BeanPropertyAssocMany lazyLoadMany = (query == null) ? null : query.getLazyLoadForParentsProperty(); - boolean withId = !subQuery && (query == null || !query.isDistinct()); + boolean withId = !rawNoId && !subQuery && (query == null || !query.isDistinct()); return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany); } else if (prop instanceof BeanPropertyAssocMany) { diff --git a/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java b/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java index df764e481..4ffb14496 100644 --- a/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java +++ b/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java @@ -4,6 +4,7 @@ import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.RawSql; import com.avaje.ebean.RawSqlBuilder; +import com.avaje.tests.model.basic.EBasic; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.OrderDetail; import com.avaje.tests.model.basic.ResetBasicData; @@ -11,6 +12,10 @@ import org.junit.Test; import java.util.List; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + public class TestRawSqlMasterDetail extends BaseTestCase { @Test @@ -77,6 +82,48 @@ public class TestRawSqlMasterDetail extends BaseTestCase { } + @Test + public void testWithNoIdPropertyWithInsert() { + + EBasic basic = new EBasic(); + basic.setName("RawSql-NoIdTest"); + basic.setStatus(EBasic.Status.ACTIVE); + + Ebean.save(basic); + + String rs = "select b.status, b.name from e_basic b "; + + RawSql rawSql = RawSqlBuilder.parse(rs).create(); + + List list = Ebean.find(EBasic.class) + .setRawSql(rawSql) + .where().eq("name", "RawSql-NoIdTest") + .findList(); + + assertEquals(1, list.size()); + EBasic basic1 = list.get(0); + basic1.setDescription("insertAfterRawFetch"); + + Ebean.insert(basic1); + } + + @Test + public void testWithNoIdProperty() { + + ResetBasicData.reset(); + + String rs = "select o.status, o.order_date from o_order o "; + + RawSql rawSql = RawSqlBuilder.parse(rs) + .create(); + + List ordersFromRaw = Ebean.find(Order.class) + .setRawSql(rawSql) + .findList(); + + assertNotNull(ordersFromRaw); + assertFalse(ordersFromRaw.isEmpty()); + } @Test public void testWithMultipleManys() {