cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject svn commit: r1420322 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/access/ test/java/org/apache/cayenne/access/ test/resources/org/apache/cayenne/access/
Date Tue, 11 Dec 2012 18:22:37 GMT
Author: aadamchik
Date: Tue Dec 11 18:22:35 2012
New Revision: 1420322

URL: http://svn.apache.org/viewvc?rev=1420322&view=rev
Log:
CAY-1779  Flatten object entities for many to many relationships on reverse engineering

patch by Ilya Drabenia - combined

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/ManyToManyCandidateEntityTest.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/cayenne-relationship-optimisation.xml
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/relationship-optimisation.map.xml
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DbLoader.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DbLoader.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DbLoader.java?rev=1420322&r1=1420321&r2=1420322&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DbLoader.java
(original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DbLoader.java
Tue Dec 11 18:22:35 2012
@@ -36,6 +36,7 @@ import java.util.Set;
 import org.apache.cayenne.CayenneException;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.TypesMapping;
+
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
@@ -121,7 +122,7 @@ public class DbLoader {
 
     /**
      * Creates new DbLoader with specified naming strategy.
-     * 
+     *
      * @since 3.0
      */
     public DbLoader(Connection connection, DbAdapter adapter,
@@ -152,7 +153,7 @@ public class DbLoader {
     /**
      * Returns true if the generator should map all primary key columns as
      * ObjAttributes.
-     * 
+     *
      * @since 3.0
      */
     public boolean isCreatingMeaningfulPK() {
@@ -161,7 +162,7 @@ public class DbLoader {
 
     /**
      * Returns database connection used by this DbLoader.
-     * 
+     *
      * @since 3.0
      */
     public Connection getConnection() {
@@ -174,7 +175,7 @@ public class DbLoader {
      * {@link org.apache.cayenne.CayenneDataObject}. If generic class name is
      * null (which is the default), DbLoader will assign each entity a unique
      * class name derived from the table name.
-     * 
+     *
      * @since 1.2
      */
     public String getGenericClassName() {
@@ -187,7 +188,7 @@ public class DbLoader {
      * {@link org.apache.cayenne.CayenneDataObject}. If generic class name is
      * set to null (which is the default), DbLoader will assign each entity a
      * unique class name derived from the table name.
-     * 
+     *
      * @since 1.2
      */
     public void setGenericClassName(String genericClassName) {
@@ -196,7 +197,7 @@ public class DbLoader {
 
     /**
      * Returns DbAdapter associated with this DbLoader.
-     * 
+     *
      * @since 1.1
      */
     public DbAdapter getAdapter() {
@@ -213,7 +214,7 @@ public class DbLoader {
 
     /**
      * Retrieves catalogs for the database associated with this DbLoader.
-     * 
+     *
      * @return List with the catalog names, empty Array if none found.
      */
     public List<String> getCatalogs() throws SQLException {
@@ -233,7 +234,7 @@ public class DbLoader {
 
     /**
      * Retrieves the schemas for the database.
-     * 
+     *
      * @return List with the schema names, empty Array if none found.
      */
     public List<String> getSchemas() throws SQLException {
@@ -254,7 +255,7 @@ public class DbLoader {
     /**
      * Returns all the table types for the given database. Types may be such as
      * "TABLE", "VIEW", "SYSTEM TABLE", etc.
-     * 
+     *
      * @return List of Strings, empty array if nothing found.
      */
     public List<String> getTableTypes() throws SQLException {
@@ -274,7 +275,7 @@ public class DbLoader {
     /**
      * Returns all tables for given combination of the criteria. Tables returned
      * as DbEntities without any attributes or relationships.
-     * 
+     *
      * @param catalogPattern
      *            The name of the catalog, may be null.
      * @param schemaPattern
@@ -344,7 +345,7 @@ public class DbLoader {
 
     /**
      * Loads dbEntities for the specified tables.
-     * 
+     *
      * @param map
      *            DataMap to be populated with DbEntities.
      * @param tables
@@ -557,17 +558,13 @@ public class DbLoader {
                     + objEntity.getName());
             map.addObjEntity(objEntity);
             loadedEntities.add(objEntity);
-            // added entity without attributes or relationships...
-            if (delegate != null) {
-                delegate.objEntityAdded(objEntity);
-            }
         }
 
         // update ObjEntity attributes and relationships
         EntityMergeSupport objEntityMerger = createEntityMerger(map);
         objEntityMerger.synchronizeWithDbEntities(loadedEntities);
     }
-    
+
     /**
      * @since 3.2
      */
@@ -753,6 +750,37 @@ public class DbLoader {
     }
 
     /**
+     * Method remove temporary entities from dataMap and optimize relationships
+     * @param map
+     */
+    private void optimizeObjRelationships(DataMap map) {
+        List<ObjEntity> entitiesForDelete = new ArrayList<ObjEntity>();
+
+        for (ObjEntity curEntity : map.getObjEntities()) {
+            ManyToManyCandidateEntity entity = new ManyToManyCandidateEntity(curEntity);
+
+            if (entity.isRepresentManyToManyTable()) {
+                entity.optimizeRelationships();
+                entitiesForDelete.add(curEntity);
+            }
+        }
+
+        // remove needed entities
+        for (ObjEntity curDeleteEntity : entitiesForDelete) {
+            map.removeObjEntity(curDeleteEntity.getName(), true);
+        }
+    }
+
+    private void fireObjEntitiesAddedEvents(DataMap map) {
+        for (ObjEntity curEntity : map.getObjEntities()) {
+            // notify delegate
+            if (delegate != null) {
+                delegate.objEntityAdded(curEntity);
+            }
+        }
+    }
+
+    /**
      * @since 3.2
      */
     public String[] getDefaultTableTypes() {
@@ -777,7 +805,7 @@ public class DbLoader {
      * Performs database reverse engineering and generates DataMap that contains
      * default mapping of the tables and views. By default will include regular
      * tables and views.
-     * 
+     *
      * @since 1.0.7
      * @deprecated since 3.2 use
      *             {@link #load(DataMap, String, String, String, String...)}
@@ -799,22 +827,33 @@ public class DbLoader {
      * Performs database reverse engineering and generates DataMap object that
      * contains default mapping of the tables and views. Allows to limit types
      * of tables to read.
-     * 
+     *
      * @deprecated since 3.2 use
      *             {@link #load(DataMap, String, String, String, String...)}
      *             method that supports catalogs.
      */
     public DataMap loadDataMapFromDB(String schemaPattern, String tablePattern,
             String[] tableTypes, DataMap dataMap) throws SQLException {
+        clearDataMap(dataMap);
+
         load(dataMap, null, schemaPattern, tablePattern, tableTypes);
         return dataMap;
     }
 
+    private void clearDataMap(DataMap dataMap) {
+        dataMap.clearDbEntities();
+        dataMap.clearEmbeddables();
+        dataMap.clearObjEntities();
+        dataMap.clearProcedures();
+        dataMap.clearQueries();
+        dataMap.clearResultSets();
+    }
+
     /**
      * Performs database reverse engineering to match the specified catalog,
      * schema, table name and table type patterns and fills the specified
      * DataMap object with DB and object mapping info.
-     * 
+     *
      * @since 3.2
      */
     public void load(DataMap dataMap, String catalogPattern,
@@ -830,7 +869,10 @@ public class DbLoader {
 
         if (loadDbEntities(dataMap, tables)) {
             loadDbRelationships(dataMap);
+
             loadObjEntities(dataMap);
+            optimizeObjRelationships(dataMap);
+            fireObjEntitiesAddedEvents(dataMap);
         }
     }
 
@@ -842,7 +884,7 @@ public class DbLoader {
      * currently this method is NOT CALLED from "loadDataMapFromDB" and should
      * be invoked explicitly by the user. </i>
      * </p>
-     * 
+     *
      * @since 1.1
      * @deprecated since 3.2 use
      *             {@link #loadProcedures(DataMap, String, String, String)} that
@@ -861,7 +903,7 @@ public class DbLoader {
      * currently this method is NOT CALLED from "loadDataMapFromDB" and should
      * be invoked explicitly by the user. </i>
      * </p>
-     * 
+     *
      * @since 3.2
      */
     public void loadProcedures(DataMap dataMap, String catalogPattern,
@@ -1010,7 +1052,7 @@ public class DbLoader {
 
     /**
      * Sets new naming strategy for reverse engineering
-     * 
+     *
      * @since 3.0
      */
     public void setNamingStrategy(NamingStrategy strategy) {

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java?rev=1420322&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java
(added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java
Tue Dec 11 18:22:35 2012
@@ -0,0 +1,113 @@
+package org.apache.cayenne.access;
+
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.Relationship;
+import org.apache.cayenne.util.NamedObjectFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class represent ObjEntity that may be optimized using flattened relationships
+ * as many to many table
+ */
+class ManyToManyCandidateEntity {
+    private ObjEntity entity;
+
+    public ManyToManyCandidateEntity(ObjEntity entityValue) {
+        entity = entityValue;
+    }
+
+    public ObjEntity getEntity() {
+        return entity;
+    }
+
+    private boolean isTargetEntitiesDifferent() {
+        return !getTargetEntity1().equals(getTargetEntity2());
+    }
+
+    private boolean isRelationshipsHasDependentPK() {
+        boolean isRelationship1HasDepPK = getDbRelationship1().getReverseRelationship().isToDependentPK();
+        boolean isRelationship2HasDepPK = getDbRelationship2().getReverseRelationship().isToDependentPK();
+
+        return isRelationship1HasDepPK && isRelationship2HasDepPK;
+    }
+
+    private ObjRelationship getRelationship1() {
+        List<Relationship> relationships = new ArrayList<Relationship>(entity.getRelationships());
+        return (ObjRelationship) relationships.get(0);
+    }
+
+    private ObjRelationship getRelationship2() {
+        List<Relationship> relationships = new ArrayList<Relationship>(entity.getRelationships());
+        return (ObjRelationship) relationships.get(1);
+    }
+
+    private ObjEntity getTargetEntity1() {
+        return (ObjEntity) getRelationship1().getTargetEntity();
+    }
+
+    private ObjEntity getTargetEntity2() {
+        return (ObjEntity) getRelationship2().getTargetEntity();
+    }
+
+    private DbRelationship getDbRelationship1() {
+        return getRelationship1().getDbRelationships().get(0);
+    }
+
+    private DbRelationship getDbRelationship2() {
+        return getRelationship2().getDbRelationships().get(0);
+    }
+
+    /**
+     * Method check - if current entity represent many to many temporary table
+     * @return true if current entity is represent many to many table; otherwise returns
false
+     */
+    public boolean isRepresentManyToManyTable() {
+        boolean hasTwoRelationships = entity.getRelationships().size() == 2;
+        boolean isNotHaveAttributes = entity.getAttributes().size() == 0;
+
+        return hasTwoRelationships && isNotHaveAttributes && isRelationshipsHasDependentPK()
+                && isTargetEntitiesDifferent();
+    }
+
+    private void removeRelationshipsFromTargetEntities() {
+        getTargetEntity1().removeRelationship(getRelationship1().getReverseRelationship().getName());
+        getTargetEntity2().removeRelationship(getRelationship2().getReverseRelationship().getName());
+    }
+
+    private void addFlattenedRelationship(ObjEntity srcEntity, ObjEntity dstEntity,
+                                          DbRelationship... relationshipPath) {
+        ObjRelationship newRelationship = (ObjRelationship) NamedObjectFactory.createRelationship(srcEntity,
dstEntity,
+                true);
+
+        newRelationship.setSourceEntity(srcEntity);
+        newRelationship.setTargetEntity(dstEntity);
+
+        for (DbRelationship curRelationship : relationshipPath) {
+            newRelationship.addDbRelationship(curRelationship);
+        }
+
+        srcEntity.addRelationship(newRelationship);
+    }
+
+    /**
+     * Method make direct relationships between 2 entities and remove relationships to
+     * many to many entity
+     */
+    public void optimizeRelationships() {
+        removeRelationshipsFromTargetEntities();
+
+        DbRelationship dbRelationship1 = getRelationship1().getDbRelationships().get(0);
+        DbRelationship dbRelationship2 = getRelationship2().getDbRelationships().get(0);
+
+        addFlattenedRelationship(getTargetEntity1(), getTargetEntity2(), dbRelationship1.getReverseRelationship(),
+                dbRelationship2);
+
+        addFlattenedRelationship(getTargetEntity2(), getTargetEntity1(), dbRelationship2.getReverseRelationship(),
+                dbRelationship1);
+    }
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/ManyToManyCandidateEntityTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/ManyToManyCandidateEntityTest.java?rev=1420322&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/ManyToManyCandidateEntityTest.java
(added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/ManyToManyCandidateEntityTest.java
Tue Dec 11 18:22:35 2012
@@ -0,0 +1,84 @@
+package org.apache.cayenne.access;
+
+import junit.framework.TestCase;
+import org.apache.cayenne.configuration.ConfigurationNameMapper;
+import org.apache.cayenne.configuration.ConfigurationTree;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.configuration.DataMapLoader;
+import org.apache.cayenne.configuration.DefaultConfigurationNameMapper;
+import org.apache.cayenne.configuration.XMLDataChannelDescriptorLoader;
+import org.apache.cayenne.configuration.XMLDataMapLoader;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.Relationship;
+import org.apache.cayenne.resource.URLResource;
+
+import java.net.URL;
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public class ManyToManyCandidateEntityTest extends TestCase {
+
+    private DataMap map;
+
+    @Override
+    public void setUp() throws Exception {
+        Module testModule = new Module() {
+
+            public void configure(Binder binder) {
+                binder.bind(DataMapLoader.class).to(XMLDataMapLoader.class);
+                binder.bind(ConfigurationNameMapper.class).to(
+                        DefaultConfigurationNameMapper.class);
+            }
+        };
+
+        Injector injector = DIBootstrap.createInjector(testModule);
+
+        // create and initialize loader instance to test
+        XMLDataChannelDescriptorLoader loader = new XMLDataChannelDescriptorLoader();
+        injector.injectMembers(loader);
+
+        String testConfigName = "relationship-optimisation";
+        URL url = getClass().getResource("cayenne-" + testConfigName + ".xml");
+
+        ConfigurationTree<DataChannelDescriptor> tree = loader.load(new URLResource(url));
+
+        map = tree.getRootNode().getDataMap(testConfigName);
+    }
+
+    public void testMatchingForManyToManyEntity() throws Exception {
+        ObjEntity manyToManyEntity = map.getObjEntity("Table1Table2");
+
+        assertEquals(true, new ManyToManyCandidateEntity(manyToManyEntity).isRepresentManyToManyTable());
+    }
+
+    public void testMatchingForNotManyToManyEntity() throws Exception {
+        ObjEntity entity = map.getObjEntity("Table1");
+
+        assertEquals(false, new ManyToManyCandidateEntity(entity).isRepresentManyToManyTable());
+    }
+
+    public void testOptimisationForManyToManyEntity() {
+        ObjEntity manyToManyEntity = map.getObjEntity("Table1Table2");
+
+        new ManyToManyCandidateEntity(manyToManyEntity).optimizeRelationships();
+
+        ObjEntity table1Entity = map.getObjEntity("Table1");
+        ObjEntity table2Entity = map.getObjEntity("Table2");
+
+        assertEquals(1, table1Entity.getRelationships().size());
+        assertEquals(table2Entity, new ArrayList<Relationship>(table1Entity.getRelationships()).get(0)
+                .getTargetEntity());
+
+        assertEquals(1, table2Entity.getRelationships().size());
+        assertEquals(table1Entity, new ArrayList<Relationship>(table2Entity.getRelationships()).get(0)
+                .getTargetEntity());
+    }
+
+}

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/cayenne-relationship-optimisation.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/cayenne-relationship-optimisation.xml?rev=1420322&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/cayenne-relationship-optimisation.xml
(added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/cayenne-relationship-optimisation.xml
Tue Dec 11 18:22:35 2012
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain project-version="6">
+	<map name="relationship-optimisation"/>
+</domain>

Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/relationship-optimisation.map.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/relationship-optimisation.map.xml?rev=1420322&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/relationship-optimisation.map.xml
(added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/resources/org/apache/cayenne/access/relationship-optimisation.map.xml
Tue Dec 11 18:22:35 2012
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<data-map xmlns="http://cayenne.apache.org/schema/3.0/modelMap"
+	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	 xsi:schemaLocation="http://cayenne.apache.org/schema/3.0/modelMap http://cayenne.apache.org/schema/3.0/modelMap.xsd"
+	 project-version="6">
+	<property name="defaultPackage" value="com.objectstyle"/>
+	<db-entity name="table1" catalog="many_to_many_test">
+		<db-attribute name="id1" type="INTEGER" isPrimaryKey="true" isMandatory="true" length="10"/>
+		<db-attribute name="table1col" type="VARCHAR" length="45"/>
+	</db-entity>
+	<db-entity name="table1_table2" catalog="many_to_many_test">
+		<db-attribute name="fk1" type="INTEGER" isPrimaryKey="true" isMandatory="true" length="10"/>
+		<db-attribute name="fk2" type="INTEGER" isPrimaryKey="true" isMandatory="true" length="10"/>
+	</db-entity>
+	<db-entity name="table2" catalog="many_to_many_test">
+		<db-attribute name="id2" type="INTEGER" isPrimaryKey="true" isMandatory="true" length="10"/>
+		<db-attribute name="table2col" type="VARCHAR" length="45"/>
+	</db-entity>
+	<obj-entity name="Table1" className="com.objectstyle.Table1" dbEntityName="table1">
+		<obj-attribute name="table1col" type="java.lang.String" db-attribute-path="table1col"/>
+	</obj-entity>
+	<obj-entity name="Table1Table2" className="com.objectstyle.Table1Table2" dbEntityName="table1_table2">
+	</obj-entity>
+	<obj-entity name="Table2" className="com.objectstyle.Table2" dbEntityName="table2">
+		<obj-attribute name="table2col" type="java.lang.String" db-attribute-path="table2col"/>
+	</obj-entity>
+	<db-relationship name="table1Table2Array" source="table1" target="table1_table2" toDependentPK="true"
toMany="true">
+		<db-attribute-pair source="id1" target="fk1"/>
+	</db-relationship>
+	<db-relationship name="toTable1" source="table1_table2" target="table1" toMany="false">
+		<db-attribute-pair source="fk1" target="id1"/>
+	</db-relationship>
+	<db-relationship name="toTable2" source="table1_table2" target="table2" toMany="false">
+		<db-attribute-pair source="fk2" target="id2"/>
+	</db-relationship>
+	<db-relationship name="table1Table2Array" source="table2" target="table1_table2" toDependentPK="true"
toMany="true">
+		<db-attribute-pair source="id2" target="fk2"/>
+	</db-relationship>
+	<obj-relationship name="table1Table2Array" source="Table1" target="Table1Table2" deleteRule="Deny"
db-relationship-path="table1Table2Array"/>
+	<obj-relationship name="toTable1" source="Table1Table2" target="Table1" deleteRule="Nullify"
db-relationship-path="toTable1"/>
+	<obj-relationship name="toTable2" source="Table1Table2" target="Table2" deleteRule="Nullify"
db-relationship-path="toTable2"/>
+	<obj-relationship name="table1Table2Array" source="Table2" target="Table1Table2" deleteRule="Deny"
db-relationship-path="table1Table2Array"/>
+</data-map>



Mime
View raw message