You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2008/12/26 12:50:31 UTC

svn commit: r729484 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select: EntitySegmentBuilder.java EntityTreeRowReader.java SelectDescriptorBuilder.java

Author: aadamchik
Date: Fri Dec 26 03:50:31 2008
New Revision: 729484

URL: http://svn.apache.org/viewvc?rev=729484&view=rev
Log:
CAY-1162 SelectDescriptor concept

support for inheritance

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntityTreeRowReader.java
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntitySegmentBuilder.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/SelectDescriptorBuilder.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntitySegmentBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntitySegmentBuilder.java?rev=729484&r1=729483&r2=729484&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntitySegmentBuilder.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntitySegmentBuilder.java Fri Dec 26 03:50:31 2008
@@ -19,12 +19,10 @@
 package org.apache.cayenne.access.select;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.map.DbAttribute;
@@ -33,7 +31,6 @@
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjRelationship;
-import org.apache.cayenne.query.EntityResultSegment;
 import org.apache.cayenne.query.QueryMetadata;
 import org.apache.cayenne.reflect.ClassDescriptor;
 
@@ -44,220 +41,144 @@
 
     private QueryMetadata metadata;
     private ExtendedTypeMap extendedTypes;
-
-    EntitySegmentBuilder(ExtendedTypeMap extendedTypes, QueryMetadata metadata) {
-        this.extendedTypes = extendedTypes;
+    private List<EntitySelectColumn> columns;
+    private Map<DbAttribute, Integer> columnMap;
+    private DbEntity dbEntity;
+    private ClassDescriptor classDescriptor;
+
+    EntitySegmentBuilder(QueryMetadata metadata, ExtendedTypeMap extendedTypes,
+            ClassDescriptor classDescriptor, DbEntity dbEntity) {
+
+        this.columns = new ArrayList<EntitySelectColumn>();
+        this.columnMap = new HashMap<DbAttribute, Integer>();
+        this.dbEntity = dbEntity;
+        this.classDescriptor = classDescriptor;
         this.metadata = metadata;
+        this.extendedTypes = extendedTypes;
     }
 
-    SelectDescriptor<Object> getSegment(int position) {
-
-        ClassDescriptor rootDescriptor;
-        EntityResultSegment segmentMetadata;
-
-        List<Object> segmentDesriptors = metadata.getResultSetMapping();
-        if (segmentDesriptors != null) {
-            segmentMetadata = (EntityResultSegment) segmentDesriptors.get(position);
-            rootDescriptor = segmentMetadata.getClassDescriptor();
-        }
-        else {
-            segmentMetadata = null;
-            rootDescriptor = metadata.getClassDescriptor();
-        }
-
-        // no ObjEntity and Java class at the root of the query...
-        if (rootDescriptor == null) {
-            DbEntity dbEntity = metadata.getDbEntity();
-            if (dbEntity == null) {
-                throw new CayenneRuntimeException("Invalid entity segment in position "
-                        + position
-                        + ", no root DbEntity specified");
-            }
-
-            return forDbEntity(dbEntity, segmentMetadata);
-        }
-
-        return forEntity(rootDescriptor, segmentMetadata);
-    }
-
-    private SelectDescriptor<Object> forEntity(
-            ClassDescriptor rootDescriptor,
-            EntityResultSegment segmentMetadata) {
-
-        Collection<DbEntity> unionRoots = rootDescriptor.getRootDbEntities();
-        if (unionRoots.size() == 1) {
-            return forSingleSelectEntity(rootDescriptor, segmentMetadata, unionRoots
-                    .iterator()
-                    .next());
-        }
-        else {
-            return forUnionSelectEntity(rootDescriptor, segmentMetadata, unionRoots);
-        }
-
-    }
-
-    private SelectDescriptor<Object> forSingleSelectEntity(
-            ClassDescriptor rootDescriptor,
-            EntityResultSegment segmentMetadata,
-            DbEntity root) {
-
-        EntityColumnAppender appender = new EntityColumnAppender(rootDescriptor, root);
-
+    SelectDescriptor<Object> getDescriptor() {
         if (metadata.getPageSize() > 0) {
-            appender.appendId();
+            appendId();
         }
         else {
-            appender.appendAll();
+            appendAll();
         }
 
         RowReader<Object> rowReader;
         // read single column ID as scalar
-        if (metadata.getPageSize() > 0 && appender.columns.size() == 1) {
-            EntitySelectColumn column = appender.columns.get(0);
+        if (metadata.getPageSize() > 0 && columns.size() == 1) {
+            EntitySelectColumn column = columns.get(0);
             rowReader = new ScalarRowReader(column.getConverter(), column.getJdbcType());
         }
         else {
             rowReader = new EntityRowReader(
-                    rootDescriptor.getEntity().getName(),
-                    appender.columns);
+                    classDescriptor.getEntity().getName(),
+                    columns);
         }
 
-        return new EntitySegment(rowReader, appender.columns);
+        return new EntitySegment(rowReader, columns);
     }
 
-    private SelectDescriptor<Object> forUnionSelectEntity(
-            ClassDescriptor rootDescriptor,
-            EntityResultSegment segmentMetadata,
-            Collection<DbEntity> unionRoots) {
-        // TODO: union query
-        throw new UnsupportedOperationException("TODO: union query");
+    private void appendId() {
+        // append meaningful attributes prior to any special DbAttributes; this way if
+        // there is an overlap between meaningful and Db attributes, the right Java
+        // type will be used.
+        appendIdObjAttributes();
+        appendIdDbAttributes();
     }
 
-    private SelectDescriptor<Object> forDbEntity(
-            DbEntity dbEntity,
-            EntityResultSegment segmentMetadata) {
-        // TODO - queries with DbEntity root
-        throw new UnsupportedOperationException("TODO");
-    }
+    private void appendAll() {
+        // append meaningful attributes prior to any special DbAttributes; this way if
+        // there is an overlap between meaningful and Db attributes, the right Java
+        // type will be used.
+        appendObjAttributes();
 
-    class EntityColumnAppender {
+        appendIdDbAttributes();
+        appendFK();
+        appendJointPrefetches();
+    }
 
-        List<EntitySelectColumn> columns;
-        Map<DbAttribute, Integer> columnMap;
-        DbEntity dbEntity;
-        ClassDescriptor classDescriptor;
+    private void appendIdObjAttributes() {
+        for (ObjAttribute attribute : classDescriptor.getEntity().getDeclaredAttributes()) {
 
-        EntityColumnAppender(ClassDescriptor classDescriptor, DbEntity dbEntity) {
-            this.columns = new ArrayList<EntitySelectColumn>();
-            this.columnMap = new HashMap<DbAttribute, Integer>();
-            this.dbEntity = dbEntity;
-            this.classDescriptor = classDescriptor;
+            if (attribute.isPrimaryKey()) {
+                appendObjAttribute(attribute);
+            }
         }
+    }
 
-        void appendId() {
-            // append meaningful attributes prior to any special DbAttributes; this way if
-            // there is an overlap between meaningful and Db attributes, the right Java
-            // type will be used.
-            appendIdObjAttributes();
-            appendIdDbAttributes();
+    private void appendObjAttributes() {
+        for (ObjAttribute attribute : classDescriptor.getEntity().getDeclaredAttributes()) {
+            appendObjAttribute(attribute);
         }
+    }
 
-        void appendAll() {
-            // append meaningful attributes prior to any special DbAttributes; this way if
-            // there is an overlap between meaningful and Db attributes, the right Java
-            // type will be used.
-            appendObjAttributes();
-
-            appendIdDbAttributes();
-            appendFK();
-            appendJointPrefetches();
+    private void appendIdDbAttributes() {
+        for (DbAttribute attribute : dbEntity.getPrimaryKeys()) {
+            appendDbAttrribute(attribute);
         }
+    }
 
-        private void appendIdObjAttributes() {
-            for (ObjAttribute attribute : classDescriptor
-                    .getEntity()
-                    .getDeclaredAttributes()) {
+    private void appendFK() {
+        for (ObjRelationship relationship : classDescriptor
+                .getEntity()
+                .getDeclaredRelationships()) {
 
-                if (attribute.isPrimaryKey()) {
-                    appendObjAttribute(attribute);
-                }
-            }
-        }
+            DbRelationship dbRel = relationship.getDbRelationships().get(0);
 
-        private void appendObjAttributes() {
-            for (ObjAttribute attribute : classDescriptor
-                    .getEntity()
-                    .getDeclaredAttributes()) {
-                appendObjAttribute(attribute);
+            List<DbJoin> joins = dbRel.getJoins();
+            int len = joins.size();
+            for (int i = 0; i < len; i++) {
+                appendDbAttrribute(joins.get(i).getSource());
             }
         }
+    }
 
-        private void appendIdDbAttributes() {
-            for (DbAttribute attribute : dbEntity.getPrimaryKeys()) {
-                appendDbAttrribute(attribute);
-            }
+    private void appendJointPrefetches() {
+        if (metadata.getPrefetchTree() != null) {
+            throw new UnsupportedOperationException("TODO: joint prefetches");
         }
+    }
 
-        private void appendFK() {
-            for (ObjRelationship relationship : classDescriptor
-                    .getEntity()
-                    .getDeclaredRelationships()) {
-
-                DbRelationship dbRel = relationship.getDbRelationships().get(0);
-
-                List<DbJoin> joins = dbRel.getJoins();
-                int len = joins.size();
-                for (int i = 0; i < len; i++) {
-                    appendDbAttrribute(joins.get(i).getSource());
-                }
-            }
-        }
+    private void appendObjAttribute(ObjAttribute attribute) {
+        EntitySelectColumn column = new EntitySelectColumn();
 
-        private void appendJointPrefetches() {
-            if (metadata.getPrefetchTree() != null) {
-                throw new UnsupportedOperationException("TODO: joint prefetches");
-            }
-        }
+        DbAttribute dbAttribute = attribute.getDbAttribute();
 
-        private void appendObjAttribute(ObjAttribute attribute) {
-            EntitySelectColumn column = new EntitySelectColumn();
-
-            DbAttribute dbAttribute = attribute.getDbAttribute();
+        // void column
+        if (dbAttribute == null) {
+            int jdbcType = TypesMapping.getSqlTypeByJava(attribute.getType());
+            column.setColumnName(TypesMapping.isNumeric(jdbcType) ? "1" : "'1'");
+            column.setJdbcType(jdbcType);
+        }
+        else {
+            column.setColumnName(dbAttribute.getName());
+            column.setJdbcType(dbAttribute.getType());
+        }
 
-            // void column
-            if (dbAttribute == null) {
-                int jdbcType = TypesMapping.getSqlTypeByJava(attribute.getType());
-                column.setColumnName(TypesMapping.isNumeric(jdbcType) ? "1" : "'1'");
-                column.setJdbcType(jdbcType);
-            }
-            else {
-                column.setColumnName(dbAttribute.getName());
-                column.setJdbcType(dbAttribute.getType());
-            }
+        column.setDataRowKey(attribute.getDbAttributePath());
+        column.setPath(attribute);
+        column.setConverter(extendedTypes.getRegisteredType(attribute.getType()));
 
-            column.setDataRowKey(attribute.getDbAttributePath());
-            column.setPath(attribute);
-            column.setConverter(extendedTypes.getRegisteredType(attribute.getType()));
+        columnMap.put(dbAttribute, columns.size());
+        columns.add(column);
+    }
 
-            columnMap.put(dbAttribute, columns.size());
-            columns.add(column);
-        }
+    private void appendDbAttrribute(DbAttribute attribute) {
+        // skip if already appended via ObjAttributes
+        if (!columnMap.containsKey(attribute)) {
 
-        private void appendDbAttrribute(DbAttribute attribute) {
-            // skip if already appended via ObjAttributes
-            if (!columnMap.containsKey(attribute)) {
-
-                EntitySelectColumn column = new EntitySelectColumn();
-                column.setColumnName(attribute.getName());
-                column.setJdbcType(attribute.getType());
-                column.setDataRowKey(attribute.getName());
+            EntitySelectColumn column = new EntitySelectColumn();
+            column.setColumnName(attribute.getName());
+            column.setJdbcType(attribute.getType());
+            column.setDataRowKey(attribute.getName());
 
-                String javaType = TypesMapping.getJavaBySqlType(attribute.getType());
-                column.setConverter(extendedTypes.getRegisteredType(javaType));
+            String javaType = TypesMapping.getJavaBySqlType(attribute.getType());
+            column.setConverter(extendedTypes.getRegisteredType(javaType));
 
-                columnMap.put(attribute, columns.size());
-                columns.add(column);
-            }
+            columnMap.put(attribute, columns.size());
+            columns.add(column);
         }
     }
-}
+}
\ No newline at end of file

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntityTreeRowReader.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntityTreeRowReader.java?rev=729484&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntityTreeRowReader.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/EntityTreeRowReader.java Fri Dec 26 03:50:31 2008
@@ -0,0 +1,70 @@
+/*****************************************************************
+ *   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.apache.cayenne.access.select;
+
+import java.sql.ResultSet;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneException;
+import org.apache.cayenne.exp.Expression;
+
+/**
+ * Inheritance-aware entity RowReader.
+ * 
+ * @since 3.0
+ */
+class EntityTreeRowReader implements RowReader<Object> {
+
+    private RowReader<?> discriminatorReader;
+    private Expression[] discriminatorExpressions;
+    private RowReader<Object>[] rowReaders;
+
+    EntityTreeRowReader(RowReader<?> discriminatorReader,
+            Expression[] discriminatorExpressions, RowReader<Object>[] rowReaders) {
+
+        this.discriminatorReader = discriminatorReader;
+        this.discriminatorExpressions = discriminatorExpressions;
+        this.rowReaders = rowReaders;
+    }
+
+    public void setColumnOffset(int offset) {
+        throw new UnsupportedOperationException("TODO");
+    }
+
+    public Object readRow(ResultSet resultSet) throws CayenneException {
+
+        Map<String, Object> discriminator = (Map<String, Object>) discriminatorReader
+                .readRow(resultSet);
+
+        int len = discriminatorExpressions.length;
+        for (int i = 0; i < len; i++) {
+
+            // TODO: andrus, 12/25/2008 - Expression in-memory evaluation for each row in
+            // a ResultSet will be a very slow operation. This procedure should be
+            // optimized somehow...
+            if (discriminatorExpressions[i].match(discriminator)) {
+                return rowReaders[i].readRow(resultSet);
+            }
+        }
+
+        throw new CayenneException(
+                "Row discriminator did not match any entities in the inheritance hierarchy: "
+                        + discriminator);
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/SelectDescriptorBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/SelectDescriptorBuilder.java?rev=729484&r1=729483&r2=729484&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/SelectDescriptorBuilder.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/select/SelectDescriptorBuilder.java Fri Dec 26 03:50:31 2008
@@ -22,8 +22,10 @@
 
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.query.EntityResultSegment;
 import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.reflect.ClassDescriptor;
 
 /**
  * A class used as a builder of SelectDescriptors of any complexity.
@@ -33,42 +35,42 @@
 public class SelectDescriptorBuilder {
 
     private ExtendedTypeMap extendedTypes;
-
     private transient ScalarSegmentBuilder scalarSegmentBuilder;
-    private transient EntitySegmentBuilder entitySegmentBuilder;
 
     public SelectDescriptorBuilder(ExtendedTypeMap extendedTypes) {
         this.extendedTypes = extendedTypes;
     }
 
-    public SelectDescriptor<?> fromQueryMetadata(QueryMetadata queryMetadata) {
+    public SelectDescriptor<?> fromQueryMetadata(QueryMetadata metadata) {
 
-        List<Object> segmentDescriptors = queryMetadata.getResultSetMapping();
-        int resultWidth = segmentDescriptors != null ? segmentDescriptors.size() : 1;
+        List<Object> segmentMap = metadata.getResultSetMapping();
+        int segmentsCount = segmentMap != null ? segmentMap.size() : 1;
 
-        if (resultWidth == 0) {
+        if (segmentsCount == 0) {
             throw new CayenneRuntimeException("Empty result descriptor");
         }
 
         int entitySegments = 0, scalarSegments = 0;
-        SelectDescriptor<Object>[] segments = new SelectDescriptor[resultWidth];
+        SelectDescriptor<Object>[] segments = new SelectDescriptor[segmentsCount];
 
-        if (segmentDescriptors == null) {
-            segments[0] = getEntitySegmentBuilder(queryMetadata).getSegment(0);
+        if (segmentMap == null) {
+            segments[0] = getEntitySegment(metadata, null, 0);
             entitySegments++;
         }
         else {
-            for (int i = 0; i < resultWidth; i++) {
+            for (int i = 0; i < segmentsCount; i++) {
 
-                Object segmentDescriptor = segmentDescriptors.get(i);
+                Object segmentDescriptor = segmentMap.get(i);
 
                 if (segmentDescriptor instanceof EntityResultSegment) {
-                    segments[i] = getEntitySegmentBuilder(queryMetadata).getSegment(i);
+                    segments[i] = getEntitySegment(
+                            metadata,
+                            (EntityResultSegment) segmentDescriptor,
+                            i);
                     entitySegments++;
                 }
                 else {
-                    segments[i] = getScalarSegmentBuilder(segmentDescriptors).getSegment(
-                            i);
+                    segments[i] = getScalarSegment(segmentMap, i);
                     scalarSegments++;
                 }
             }
@@ -76,7 +78,7 @@
 
         // sanity check - paginated queries are only possible if there is an "id" of each
         // row. for now this means single entity queries...
-        if (queryMetadata.getPageSize() > 0) {
+        if (metadata.getPageSize() > 0) {
             if (entitySegments != 1 || scalarSegments != 0) {
                 throw new CayenneRuntimeException(
                         "Paginated queries are only supported for a single entity result");
@@ -84,26 +86,63 @@
         }
 
         // do some small optimizations for the common 1 segment results...
-        if (resultWidth == 1) {
+        if (segmentsCount == 1) {
             return segments[0];
         }
 
         return new CompoundSelectDescriptor(segments);
     }
 
-    protected EntitySegmentBuilder getEntitySegmentBuilder(QueryMetadata queryMetadata) {
-        if (entitySegmentBuilder == null) {
-            entitySegmentBuilder = new EntitySegmentBuilder(extendedTypes, queryMetadata);
+    protected SelectDescriptor<Object> getEntitySegment(
+            QueryMetadata metadata,
+            EntityResultSegment segmentDescriptor,
+            int position) {
+
+        ClassDescriptor classDescriptor;
+        EntityResultSegment segmentMetadata;
+
+        List<Object> segmentDesriptors = metadata.getResultSetMapping();
+        if (segmentDesriptors != null) {
+            segmentMetadata = (EntityResultSegment) segmentDesriptors.get(position);
+            classDescriptor = segmentMetadata.getClassDescriptor();
+        }
+        else {
+            segmentMetadata = null;
+            classDescriptor = metadata.getClassDescriptor();
+        }
+
+        // no ObjEntity and Java class at the root of the query...
+        if (classDescriptor == null) {
+            DbEntity dbEntity = metadata.getDbEntity();
+            if (dbEntity == null) {
+                throw new CayenneRuntimeException("Invalid entity segment in position "
+                        + position
+                        + ", no root DbEntity specified");
+            }
+
+            throw new UnsupportedOperationException("TODO: DbEntity based queries");
+        }
+
+        if (classDescriptor.getEntityInheritanceTree() != null) {
+            throw new UnsupportedOperationException("TODO: Inheritance aware queries");
+        }
+        else {
+            return new EntitySegmentBuilder(
+                    metadata,
+                    extendedTypes,
+                    classDescriptor,
+                    classDescriptor.getEntity().getDbEntity()).getDescriptor();
         }
-        return entitySegmentBuilder;
     }
 
-    protected ScalarSegmentBuilder getScalarSegmentBuilder(List<Object> segmentDescriptors) {
+    protected SelectDescriptor<Object> getScalarSegment(
+            List<Object> segmentDescriptors,
+            int position) {
         if (scalarSegmentBuilder == null) {
             scalarSegmentBuilder = new ScalarSegmentBuilder(
                     extendedTypes,
                     segmentDescriptors);
         }
-        return scalarSegmentBuilder;
+        return scalarSegmentBuilder.getSegment(position);
     }
 }