Browse Source

AMBARI-11788 - Views : Data storage error when try to execute some query in hive view (tbeerbower)

tbeerbower 10 years ago
parent
commit
59fa63f183

+ 30 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java

@@ -144,6 +144,14 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
   @Basic
   private char xmlDriven = 'N';
 
+  /**
+   * Indicates whether or not to alter the names of the data store entities to
+   * avoid db reserved word conflicts.
+   */
+  @Column(name = "alter_names")
+  @Basic
+  private Integer alterNames;
+
   /**
    * The instance properties.
    */
@@ -224,6 +232,7 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
     this.description = instanceConfig.getDescription();
     this.clusterHandle = null;
     this.visible = instanceConfig.isVisible() ? 'Y' : 'N';
+    this.alterNames = 1;
 
     String label = instanceConfig.getLabel();
     this.label = (label == null || label.length() == 0) ? view.getLabel() : label;
@@ -260,6 +269,7 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
     this.description = null;
     this.clusterHandle = null;
     this.visible = 'Y';
+    this.alterNames = 1;
     this.label = label;
   }
 
@@ -471,6 +481,26 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
     this.xmlDriven = (xmlDriven) ? 'Y' : 'N';
   }
 
+  /**
+   * Determine whether or not to alter the names of the
+   * data store entities to avoid db reserved word conflicts.
+   *
+   * @return true if the view is a system view
+   */
+  public boolean alterNames() {
+    return alterNames == 1;
+  }
+
+  /**
+   * Set the flag which indicates whether or not to alter the names of the
+   * data store entities to avoid db reserved word conflicts.
+   *
+   * @param alterNames  the alterNames flag; true if the data store names should be altered
+   */
+  public void setAlterNames(boolean alterNames) {
+    this.alterNames = alterNames ? 1 : 0;
+  }
+
   /**
    * Get the instance properties.
    *

+ 12 - 4
ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog210.java

@@ -171,6 +171,7 @@ public class UpgradeCatalog210 extends AbstractUpgradeCatalog {
     executeWidgetDDLUpdates();
     executeStackDDLUpdates();
     executeTopologyDDLUpdates();
+    executeViewDDLUpdates();
   }
 
   private void executeTopologyDDLUpdates() throws AmbariException, SQLException {
@@ -274,6 +275,17 @@ public class UpgradeCatalog210 extends AbstractUpgradeCatalog {
 
   }
 
+  private void executeViewDDLUpdates() throws AmbariException, SQLException {
+    // cluster association
+    dbAccessor.addColumn(VIEW_INSTANCE_TABLE, new DBColumnInfo("cluster_handle", String.class, 255, null, true));
+    // determine whether to alter the names of the dynamic entities / attributes to
+    // avoid db reserved word conflicts.  should be false for existing instances
+    // for backward compatibility.
+    dbAccessor.addColumn(VIEW_INSTANCE_TABLE, new DBColumnInfo("alter_names", Integer.class, null, 0, false));
+    // cluster configuration
+    dbAccessor.addColumn(VIEW_PARAMETER_TABLE, new DBColumnInfo("cluster_config", String.class, 255, null, true));
+  }
+
   /**
    * Execute all of the hosts DDL updates.
    *
@@ -498,10 +510,6 @@ public class UpgradeCatalog210 extends AbstractUpgradeCatalog {
     // Notice that the column name doesn't have an underscore here.
     dbAccessor.dropColumn(SERVICE_CONFIG_HOSTS_TABLE, "hostname");
 
-    // view columns for cluster association
-    dbAccessor.addColumn(VIEW_INSTANCE_TABLE, new DBColumnInfo("cluster_handle", String.class, 255, null, true));
-    dbAccessor.addColumn(VIEW_PARAMETER_TABLE, new DBColumnInfo("cluster_config", String.class, 255, null, true));
-
     // Update host names to be case insensitive
     String UPDATE_TEMPLATE = "UPDATE {0} SET {1} = lower({1})";
     // First remove duplicate hosts

+ 6 - 4
ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java

@@ -520,10 +520,12 @@ public class ViewRegistry {
 
         ResourceTypeEntity resourceTypeEntity = resourceTypeDAO.findByName(ViewEntity.getViewName(viewName, version));
 
+        setPersistenceEntities(instanceEntity);
+
         ViewInstanceEntity persistedInstance = mergeViewInstance(instanceEntity, resourceTypeEntity);
 
         instanceEntity.setViewInstanceId(persistedInstance.getViewInstanceId());
-        instanceEntity.setResource(persistedInstance.getResource());
+        syncViewInstance(instanceEntity, persistedInstance);
 
         try {
           // bind the view instance to a view
@@ -1111,6 +1113,9 @@ public class ViewRegistry {
       properties.put(propertyConfig.getKey(), propertyConfig.getValue());
     }
     setViewInstanceProperties(viewInstanceDefinition, properties, viewConfig, viewDefinition.getClassLoader());
+
+    setPersistenceEntities(viewInstanceDefinition);
+
     return viewInstanceDefinition;
   }
 
@@ -1147,9 +1152,6 @@ public class ViewRegistry {
             getProvider(resourceConfig.getProviderClass(cl), viewInstanceContext));
       }
     }
-
-    setPersistenceEntities(viewInstanceDefinition);
-
     viewDefinition.addInstanceDefinition(viewInstanceDefinition);
   }
 

+ 66 - 30
ambari-server/src/main/java/org/apache/ambari/server/view/persistence/DataStoreImpl.java

@@ -44,6 +44,7 @@ import java.lang.reflect.ParameterizedType;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -89,17 +90,17 @@ public class DataStoreImpl implements DataStore {
   /**
    * Map of dynamic entity names keyed by view entity class.
    */
-  private final Map<Class, String> entityClassMap = new HashMap<Class, String>();
+  private final Map<Class, String> entityClassMap = new LinkedHashMap<Class, String>();
 
   /**
    * Map of entity primary key fields keyed by dynamic entity name.
    */
-  private final Map<String, ViewEntityEntity> entityMap = new HashMap<String, ViewEntityEntity>();
+  private final Map<String, ViewEntityEntity> entityMap = new LinkedHashMap<String, ViewEntityEntity>();
 
   /**
    * Map of dynamic entity type builders keyed by dynamic entity name.
    */
-  private final Map<String, JPADynamicTypeBuilder> typeBuilderMap = new HashMap<String, JPADynamicTypeBuilder>();
+  private final Map<String, JPADynamicTypeBuilder> typeBuilderMap = new LinkedHashMap<String, JPADynamicTypeBuilder>();
 
   /**
    * Indicates whether or not the data store has been initialized.
@@ -111,6 +112,11 @@ public class DataStoreImpl implements DataStore {
    */
   protected final static Logger LOG = LoggerFactory.getLogger(DataStoreImpl.class);
 
+  /**
+   * Table / column name prefix.
+   */
+  private static final String NAME_PREFIX = "DS_";
+
 
   // ----- DataStore ---------------------------------------------------------
 
@@ -278,17 +284,20 @@ public class DataStoreImpl implements DataStore {
       Map<String, PropertyDescriptor> descriptorMap = getDescriptorMap(clazz);
 
       for (Map.Entry<String, PropertyDescriptor> descriptorEntry : descriptorMap.entrySet()) {
-        String             propertyName = descriptorEntry.getKey();
-        PropertyDescriptor descriptor   = descriptorEntry.getValue();
 
-        if (propertyName.equals(entityMap.get(entityName).getIdProperty())) {
-          typeBuilder.setPrimaryKeyFields(propertyName);
+        String fieldName     = descriptorEntry.getKey();
+        String attributeName = getAttributeName(fieldName);
+
+        PropertyDescriptor descriptor = descriptorEntry.getValue();
+
+        if (fieldName.equals(entityMap.get(entityName).getIdProperty())) {
+          typeBuilder.setPrimaryKeyFields(attributeName);
         }
 
         Class<?> propertyType = descriptor.getPropertyType();
 
         if (isDirectMappingType(propertyType)) {
-          typeBuilder.addDirectMapping(propertyName, propertyType, propertyName);
+          typeBuilder.addDirectMapping(attributeName, propertyType, attributeName);
         }
       }
     }
@@ -303,10 +312,13 @@ public class DataStoreImpl implements DataStore {
       Map<String, PropertyDescriptor> descriptorMap = getDescriptorMap(clazz);
 
       for (Map.Entry<String, PropertyDescriptor> descriptorEntry : descriptorMap.entrySet()) {
-        String propertyName = descriptorEntry.getKey();
+        String fieldName     = descriptorEntry.getKey();
+        String attributeName = getAttributeName(fieldName);
+
+
         PropertyDescriptor descriptor = descriptorEntry.getValue();
-        if (propertyName.equals(entityMap.get(entityName).getIdProperty())) {
-          typeBuilder.setPrimaryKeyFields(propertyName);
+        if (fieldName.equals(entityMap.get(entityName).getIdProperty())) {
+          typeBuilder.setPrimaryKeyFields(attributeName);
         }
 
         Class<?> propertyType = descriptor.getPropertyType();
@@ -315,23 +327,23 @@ public class DataStoreImpl implements DataStore {
         if (refEntityName == null) {
           if (Collection.class.isAssignableFrom(propertyType)) {
 
-            String tableName = getTableName(entityMap.get(entityName)) + "_" + propertyName;
+            String tableName = getTableName(entityMap.get(entityName)) + "_" + attributeName;
 
-            Class<?> parameterizedTypeClass = getParameterizedTypeClass(clazz, propertyName);
+            Class<?> parameterizedTypeClass = getParameterizedTypeClass(clazz, attributeName);
 
             refEntityName = entityClassMap.get(parameterizedTypeClass);
 
             if (refEntityName == null) {
-              typeBuilder.addDirectCollectionMapping(propertyName, tableName, propertyName,
+              typeBuilder.addDirectCollectionMapping(attributeName, tableName, attributeName,
                   parameterizedTypeClass, entityMap.get(entityName).getIdProperty());
             } else {
               DynamicType refType = typeBuilderMap.get(refEntityName).getType();
-              typeBuilder.addManyToManyMapping(propertyName, refType, tableName);
+              typeBuilder.addManyToManyMapping(attributeName, refType, tableName);
             }
           }
         } else {
           DynamicType refType = typeBuilderMap.get(refEntityName).getType();
-          typeBuilder.addOneToOneMapping(propertyName, refType, propertyName);
+          typeBuilder.addOneToOneMapping(attributeName, refType, attributeName);
         }
       }
     }
@@ -376,16 +388,19 @@ public class DataStoreImpl implements DataStore {
 
       persistSet.add(dynamicEntity);
 
-      for (String propertyName : type.getPropertiesNames()) {
-        if (properties.containsKey(propertyName)) {
-          Object value = properties.get(propertyName);
+      for (String attributeName : type.getPropertiesNames()) {
+
+        String fieldName = getFieldName(attributeName);
+
+        if (properties.containsKey(fieldName)) {
+          Object value = properties.get(fieldName);
           if (value != null) {
             Class<?> valueClass = value.getClass();
 
             if (Collection.class.isAssignableFrom(valueClass)) {
 
-              Class<?>           typeClass  = getParameterizedTypeClass(clazz, propertyName);
-              Collection<Object> collection = dynamicEntity.get(propertyName);
+              Class<?>           typeClass  = getParameterizedTypeClass(clazz, fieldName);
+              Collection<Object> collection = dynamicEntity.get(attributeName);
 
               collection.clear();
 
@@ -403,7 +418,7 @@ public class DataStoreImpl implements DataStore {
                 value = persistEntity(value, em, persistSet);
               }
               if (value != null) {
-                dynamicEntity.set(propertyName, value);
+                dynamicEntity.set(attributeName, value);
               }
             }
           }
@@ -425,8 +440,9 @@ public class DataStoreImpl implements DataStore {
 
     Map<String, Object> properties = new HashMap<String, Object>();
 
-    for (String propertyName : type.getPropertiesNames()) {
-      properties.put(propertyName, entity.get(propertyName));
+    for (String attributeName : type.getPropertiesNames()) {
+      String fieldName = getFieldName(attributeName);
+      properties.put(fieldName, entity.get(attributeName));
     }
     setEntityProperties(resource, properties);
 
@@ -453,7 +469,7 @@ public class DataStoreImpl implements DataStore {
         quoted = quoted ^ token.equals("\"");
 
         if (propertyNames.contains(token) && !quoted) {
-          stringBuilder.append(" e.").append(token);
+          stringBuilder.append(" e.").append(getAttributeName(token));
         } else {
           stringBuilder.append(token);
         }
@@ -584,20 +600,40 @@ public class DataStoreImpl implements DataStore {
   }
 
   // get a table name for the given view entity
-  private static String getTableName(ViewEntityEntity entity) {
+  private String getTableName(ViewEntityEntity entity) {
     return (getEntityName(entity)).toUpperCase();
   }
 
+  // get the java class field name from the entity attribute name
+  private String getFieldName(String attributeName) {
+    return alterNames() ? attributeName.substring(NAME_PREFIX.length()) : attributeName;
+  }
+
+  // get the entity attribute name from the java class field name
+  private String getAttributeName(String fieldName) {
+    return alterNames() ? (NAME_PREFIX + fieldName) : fieldName;
+  }
+
   // get a dynamic entity name for the given view entity
-  private static String getEntityName(ViewEntityEntity entity) {
-    String   className = entity.getClassName();
-    String[] parts     = className.split("\\.");
+  private String getEntityName(ViewEntityEntity entity) {
+    String   className     = entity.getClassName();
+    String[] parts         = className.split("\\.");
+    String simpleClassName = parts[parts.length - 1];
 
-    return parts[parts.length - 1] + entity.getId();
+    if (alterNames()) {
+      return NAME_PREFIX + simpleClassName + "_" + entity.getId();
+    }
+    return simpleClassName + entity.getId();
   }
 
   // get an entity manager
   private EntityManager getEntityManager() {
     return entityManagerFactory.createEntityManager();
   }
+
+  // determine whether to alter the names of the dynamic entities /attributes to
+  // avoid db reserved word conflicts.  return false for backward compatibility.
+  private boolean alterNames() {
+    return viewInstanceEntity.alterNames();
+  }
 }

+ 1 - 0
ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql

@@ -456,6 +456,7 @@ CREATE TABLE viewinstance (
   icon VARCHAR(255),
   icon64 VARCHAR(255),
   xml_driven CHAR(1),
+  alter_names TINYINT(1) NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
   PRIMARY KEY(view_instance_id));
 

+ 1 - 0
ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql

@@ -446,6 +446,7 @@ CREATE TABLE viewinstance (
   icon VARCHAR(255),
   icon64 VARCHAR(255),
   xml_driven CHAR(1),
+  alter_names NUMBER(1) DEFAULT 1 NOT NULL,
   cluster_handle VARCHAR(255),
   PRIMARY KEY(view_instance_id));
 

+ 1 - 0
ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql

@@ -453,6 +453,7 @@ CREATE TABLE viewinstance (
   icon VARCHAR(255),
   icon64 VARCHAR(255),
   xml_driven CHAR(1),
+  alter_names SMALLINT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
   PRIMARY KEY(view_instance_id));
 

+ 1 - 0
ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql

@@ -507,6 +507,7 @@ CREATE TABLE ambari.viewinstance (
   icon VARCHAR(255),
   icon64 VARCHAR(255),
   xml_driven CHAR(1),
+  alter_names SMALLINT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
   PRIMARY KEY(view_instance_id));
 

+ 1 - 0
ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql

@@ -527,6 +527,7 @@ CREATE TABLE viewinstance (
   icon VARCHAR(255),
   icon64 VARCHAR(255),
   xml_driven CHAR(1),
+  alter_names BIT NOT NULL DEFAULT 1,
   cluster_handle VARCHAR(255),
   PRIMARY KEY CLUSTERED (view_instance_id)
   );

+ 14 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java

@@ -249,6 +249,20 @@ public class ViewInstanceEntityTest {
     Assert.assertEquals("/this/is/the/icon/url/icon.png", viewInstanceDefinition.getIcon());
   }
 
+  @Test
+  public void testAlterNames() throws Exception {
+    InstanceConfig instanceConfig = InstanceConfigTest.getInstanceConfigs(xml_with_instance_label).get(0);
+    ViewEntity viewDefinition = ViewEntityTest.getViewEntity();
+    ViewInstanceEntity viewInstanceDefinition = new ViewInstanceEntity(viewDefinition, instanceConfig);
+    Assert.assertTrue(viewInstanceDefinition.alterNames());
+
+    viewInstanceDefinition.setAlterNames(false);
+    Assert.assertFalse(viewInstanceDefinition.alterNames());
+
+    viewInstanceDefinition.setAlterNames(true);
+    Assert.assertTrue(viewInstanceDefinition.alterNames());
+  }
+
   @Test
   public void testGetIcon64() throws Exception {
     InstanceConfig instanceConfig = InstanceConfigTest.getInstanceConfigs(xml_with_instance_label).get(0);

+ 11 - 1
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog210Test.java

@@ -557,9 +557,11 @@ public class UpgradeCatalog210Test {
       captures = new HashMap<String, Capture<DBColumnInfo>>();
 
       Capture<DBAccessor.DBColumnInfo> viewInstanceColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
+      Capture<DBAccessor.DBColumnInfo> viewInstanceAlterNamesColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
       Capture<DBAccessor.DBColumnInfo> viewParamColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
 
       captures.put("viewinstance", viewInstanceColumnCapture);
+      captures.put("viewinstance_alter_names", viewInstanceAlterNamesColumnCapture);
       captures.put("viewparameter", viewParamColumnCapture);
     }
 
@@ -569,9 +571,11 @@ public class UpgradeCatalog210Test {
     @Override
     public void execute(DBAccessor dbAccessor) throws SQLException {
       Capture<DBColumnInfo> viewInstanceColumnCapture = captures.get("viewinstance");
+      Capture<DBColumnInfo> viewInstanceAlterNamesColumnCapture = captures.get("viewinstance_alter_names");
       Capture<DBColumnInfo> viewParamColumnCapture = captures.get("viewparameter");
 
       dbAccessor.addColumn(eq("viewinstance"), capture(viewInstanceColumnCapture));
+      dbAccessor.addColumn(eq("viewinstance"), capture(viewInstanceAlterNamesColumnCapture));
       dbAccessor.addColumn(eq("viewparameter"), capture(viewParamColumnCapture));
     }
 
@@ -581,6 +585,7 @@ public class UpgradeCatalog210Test {
     @Override
     public void verify(DBAccessor dbAccessor) throws SQLException {
       verifyViewInstance(captures.get("viewinstance"));
+      verifyViewInstanceAlterNames(captures.get("viewinstance_alter_names"));
       verifyViewParameter(captures.get("viewparameter"));
     }
 
@@ -590,6 +595,11 @@ public class UpgradeCatalog210Test {
       Assert.assertEquals("cluster_handle", clusterIdColumn.getName());
     }
 
+    private void verifyViewInstanceAlterNames(Capture<DBAccessor.DBColumnInfo> viewInstanceAlterNamesColumnCapture) {
+      DBColumnInfo clusterIdColumn = viewInstanceAlterNamesColumnCapture.getValue();
+      Assert.assertEquals(Integer.class, clusterIdColumn.getType());
+      Assert.assertEquals("alter_names", clusterIdColumn.getName());
+    }
 
     private void verifyViewParameter(Capture<DBAccessor.DBColumnInfo> viewParamColumnCapture) {
       DBColumnInfo clusterConfigColumn = viewParamColumnCapture.getValue();
@@ -599,7 +609,7 @@ public class UpgradeCatalog210Test {
   }
 
   /**
-   * Verify view changes
+   * Verify alert changes
    */
   class AlertSectionDDL implements SectionDDL {
 

+ 103 - 30
ambari-server/src/test/java/org/apache/ambari/server/view/persistence/DataStoreImplTest.java

@@ -76,6 +76,10 @@ public class DataStoreImplTest {
       "        <class>org.apache.ambari.server.view.persistence.DataStoreImplTest$TestEntity</class>\n" +
       "        <id-property>id</id-property>\n" +
       "      </entity>\n" +
+      "      <entity>\n" +
+      "        <class>org.apache.ambari.server.view.persistence.DataStoreImplTest$TestSubEntity</class>\n" +
+      "        <id-property>name</id-property>\n" +
+      "      </entity>\n" +
       "    </persistence>" +
       "</view>";
 
@@ -92,15 +96,24 @@ public class DataStoreImplTest {
 
     // set expectations
     Capture<DynamicType> typeCapture = new Capture<DynamicType>();
-    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture));
+    Capture<DynamicType> typeCapture2 = new Capture<DynamicType>();
+    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture), capture(typeCapture2));
 
     expect(entityManagerFactory.createEntityManager()).andReturn(entityManager);
     expect(entityManager.getTransaction()).andReturn(transaction).anyTimes();
 
     Capture<Class> entityClassCapture = new Capture<Class>();
-    expect(entityManager.find(capture(entityClassCapture), eq(99))).andReturn(null);
+    expect(entityManager.find(capture(entityClassCapture), eq("bar"))).andReturn(null);
+
+    Capture<Class> entityClassCapture2 = new Capture<Class>();
+    expect(entityManager.find(capture(entityClassCapture2), eq(99))).andReturn(null);
+
     Capture<DynamicEntity> entityCapture = new Capture<DynamicEntity>();
     entityManager.persist(capture(entityCapture));
+
+    Capture<DynamicEntity> entityCapture2 = new Capture<DynamicEntity>();
+    entityManager.persist(capture(entityCapture2));
+
     entityManager.close();
 
     transaction.begin();
@@ -111,11 +124,15 @@ public class DataStoreImplTest {
 
     DataStoreImpl dataStore = getDataStore(entityManagerFactory, jpaDynamicHelper, classLoader, schemaManager);
 
-    dataStore.store(new TestEntity(99, "foo"));
+    dataStore.store(new TestEntity(99, "foo", new TestSubEntity("bar")));
 
     Assert.assertEquals(entityClassCapture.getValue(), typeCapture.getValue().getJavaClass());
-    Assert.assertEquals(99, entityCapture.getValue().get("id"));
-    Assert.assertEquals("foo", entityCapture.getValue().get("name"));
+    Assert.assertEquals(entityClassCapture2.getValue(), typeCapture2.getValue().getJavaClass());
+
+    Assert.assertEquals("bar", entityCapture.getValue().get("DS_name"));
+
+    Assert.assertEquals(99, entityCapture2.getValue().get("DS_id"));
+    Assert.assertEquals("foo", entityCapture2.getValue().get("DS_name"));
 
     // verify mocks
     verify(entityManagerFactory, entityManager, jpaDynamicHelper, transaction, schemaManager);
@@ -135,17 +152,28 @@ public class DataStoreImplTest {
 
     // set expectations
     Capture<DynamicType> typeCapture = new Capture<DynamicType>();
-    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture));
+    Capture<DynamicType> typeCapture2 = new Capture<DynamicType>();
+    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture), capture(typeCapture2));
 
     expect(entityManagerFactory.createEntityManager()).andReturn(entityManager);
     expect(entityManager.getTransaction()).andReturn(transaction).anyTimes();
 
     Capture<Class> entityClassCapture = new Capture<Class>();
-    expect(entityManager.find(capture(entityClassCapture), eq(99))).andReturn(dynamicEntity);
+    expect(entityManager.find(capture(entityClassCapture), eq("bar"))).andReturn(null);
+
+    Capture<Class> entityClassCapture2 = new Capture<Class>();
+    expect(entityManager.find(capture(entityClassCapture2), eq(99))).andReturn(dynamicEntity);
+
+    Capture<DynamicEntity> entityCapture = new Capture<DynamicEntity>();
+    entityManager.persist(capture(entityCapture));
+
     entityManager.close();
 
-    expect(dynamicEntity.set("id", 99)).andReturn(dynamicEntity);
-    expect(dynamicEntity.set("name", "foo")).andReturn(dynamicEntity);
+    expect(dynamicEntity.set("DS_id", 99)).andReturn(dynamicEntity);
+    expect(dynamicEntity.set("DS_name", "foo")).andReturn(dynamicEntity);
+
+    Capture<DynamicEntity> subEntityCapture = new Capture<DynamicEntity>();
+    expect(dynamicEntity.set(eq("DS_subEntity"), capture(subEntityCapture))).andReturn(dynamicEntity);
 
     transaction.begin();
     transaction.commit();
@@ -155,9 +183,10 @@ public class DataStoreImplTest {
 
     DataStoreImpl dataStore = getDataStore(entityManagerFactory, jpaDynamicHelper, classLoader, schemaManager);
 
-    dataStore.store(new TestEntity(99, "foo"));
+    dataStore.store(new TestEntity(99, "foo", new TestSubEntity("bar")));
 
     Assert.assertEquals(entityClassCapture.getValue(), typeCapture.getValue().getJavaClass());
+    Assert.assertEquals(entityClassCapture2.getValue(), typeCapture2.getValue().getJavaClass());
 
     // verify mocks
     verify(entityManagerFactory, entityManager, jpaDynamicHelper, transaction, schemaManager, dynamicEntity);
@@ -177,7 +206,8 @@ public class DataStoreImplTest {
 
     // set expectations
     Capture<DynamicType> typeCapture = new Capture<DynamicType>();
-    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture));
+    Capture<DynamicType> typeCapture2 = new Capture<DynamicType>();
+    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture), capture(typeCapture2));
 
     expect(entityManagerFactory.createEntityManager()).andReturn(entityManager);
     expect(entityManager.getTransaction()).andReturn(transaction).anyTimes();
@@ -194,9 +224,9 @@ public class DataStoreImplTest {
 
     DataStoreImpl dataStore = getDataStore(entityManagerFactory, jpaDynamicHelper, classLoader, schemaManager);
 
-    dataStore.remove(new TestEntity(99, "foo"));
+    dataStore.remove(new TestEntity(99, "foo", new TestSubEntity("bar")));
 
-    Assert.assertEquals(entityClassCapture.getValue(), typeCapture.getValue().getJavaClass());
+    Assert.assertEquals(entityClassCapture.getValue(), typeCapture2.getValue().getJavaClass());
 
     // verify mocks
     verify(entityManagerFactory, entityManager, jpaDynamicHelper, transaction, schemaManager, dynamicEntity);
@@ -215,15 +245,18 @@ public class DataStoreImplTest {
 
     // set expectations
     Capture<DynamicType> typeCapture = new Capture<DynamicType>();
-    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture));
+    Capture<DynamicType> typeCapture2 = new Capture<DynamicType>();
+    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture), capture(typeCapture2));
 
     expect(entityManagerFactory.createEntityManager()).andReturn(entityManager);
     Capture<Class> entityClassCapture = new Capture<Class>();
     expect(entityManager.find(capture(entityClassCapture), eq(99))).andReturn(dynamicEntity);
     entityManager.close();
 
-    expect(dynamicEntity.get("id")).andReturn(99);
-    expect(dynamicEntity.get("name")).andReturn("foo");
+    expect(dynamicEntity.get("DS_id")).andReturn(99);
+    expect(dynamicEntity.get("DS_name")).andReturn("foo");
+    TestSubEntity subEntity = new TestSubEntity("bar");
+    expect(dynamicEntity.get("DS_subEntity")).andReturn(subEntity);
 
     // replay mocks
     replay(entityManagerFactory, entityManager, jpaDynamicHelper, dynamicEntity, schemaManager);
@@ -232,7 +265,7 @@ public class DataStoreImplTest {
 
     TestEntity entity = dataStore.find(TestEntity.class, 99);
 
-    Assert.assertEquals(entityClassCapture.getValue(), typeCapture.getValue().getJavaClass());
+    Assert.assertEquals(entityClassCapture.getValue(), typeCapture2.getValue().getJavaClass());
     Assert.assertEquals(99, entity.getId());
     Assert.assertEquals("foo", entity.getName());
 
@@ -254,17 +287,20 @@ public class DataStoreImplTest {
 
     // set expectations
     Capture<DynamicType> typeCapture = new Capture<DynamicType>();
-    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture));
+    Capture<DynamicType> typeCapture2 = new Capture<DynamicType>();
+    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture), capture(typeCapture2));
 
     expect(entityManagerFactory.createEntityManager()).andReturn(entityManager);
     expect(entityManager.createQuery(
-        "SELECT e FROM DataStoreImplTest$TestEntity1 e WHERE e.id=99")).andReturn(query);
+        "SELECT e FROM DS_DataStoreImplTest$TestEntity_1 e WHERE e.DS_id=99")).andReturn(query);
     entityManager.close();
 
     expect(query.getResultList()).andReturn(Collections.singletonList(dynamicEntity));
 
-    expect(dynamicEntity.get("id")).andReturn(99);
-    expect(dynamicEntity.get("name")).andReturn("foo");
+    expect(dynamicEntity.get("DS_id")).andReturn(99);
+    expect(dynamicEntity.get("DS_name")).andReturn("foo");
+    TestSubEntity subEntity = new TestSubEntity("bar");
+    expect(dynamicEntity.get("DS_subEntity")).andReturn(subEntity);
 
     // replay mocks
     replay(entityManagerFactory, entityManager, jpaDynamicHelper, dynamicEntity, query, schemaManager);
@@ -300,11 +336,12 @@ public class DataStoreImplTest {
 
     // set expectations
     Capture<DynamicType> typeCapture = new Capture<DynamicType>();
-    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture));
+    Capture<DynamicType> typeCapture2 = new Capture<DynamicType>();
+    jpaDynamicHelper.addTypes(eq(true), eq(true), capture(typeCapture), capture(typeCapture2));
 
     expect(entityManagerFactory.createEntityManager()).andReturn(entityManager);
     expect(entityManager.createQuery(
-        "SELECT e FROM DataStoreImplTest$TestEntity1 e WHERE e.name='foo'")).andReturn(query);
+        "SELECT e FROM DS_DataStoreImplTest$TestEntity_1 e WHERE e.DS_name='foo'")).andReturn(query);
     entityManager.close();
 
     List<DynamicEntity> entityList = new LinkedList<DynamicEntity>();
@@ -314,14 +351,20 @@ public class DataStoreImplTest {
 
     expect(query.getResultList()).andReturn(entityList);
 
-    expect(dynamicEntity1.get("id")).andReturn(99);
-    expect(dynamicEntity1.get("name")).andReturn("foo");
+    expect(dynamicEntity1.get("DS_id")).andReturn(99);
+    expect(dynamicEntity1.get("DS_name")).andReturn("foo");
+    TestSubEntity subEntity1 = new TestSubEntity("bar");
+    expect(dynamicEntity1.get("DS_subEntity")).andReturn(subEntity1);
 
-    expect(dynamicEntity2.get("id")).andReturn(100);
-    expect(dynamicEntity2.get("name")).andReturn("foo");
+    expect(dynamicEntity2.get("DS_id")).andReturn(100);
+    expect(dynamicEntity2.get("DS_name")).andReturn("foo");
+    TestSubEntity subEntity2 = new TestSubEntity("bar");
+    expect(dynamicEntity2.get("DS_subEntity")).andReturn(subEntity2);
 
-    expect(dynamicEntity3.get("id")).andReturn(101);
-    expect(dynamicEntity3.get("name")).andReturn("foo");
+    expect(dynamicEntity3.get("DS_id")).andReturn(101);
+    expect(dynamicEntity3.get("DS_name")).andReturn("foo");
+    TestSubEntity subEntity3 = new TestSubEntity("bar");
+    expect(dynamicEntity3.get("DS_subEntity")).andReturn(subEntity3);
 
     // replay mocks
     replay(entityManagerFactory, entityManager, jpaDynamicHelper,
@@ -388,13 +431,15 @@ public class DataStoreImplTest {
     public TestEntity() {
     }
 
-    public TestEntity(int id, String name) {
+    public TestEntity(int id, String name, TestSubEntity subEntity) {
       this.id = id;
       this.name = name;
+      this.subEntity = subEntity;
     }
 
     int id;
     String name;
+    TestSubEntity subEntity;
 
     public int getId() {
       return id;
@@ -411,6 +456,34 @@ public class DataStoreImplTest {
     public void setName(String name) {
       this.name = name;
     }
+
+    public TestSubEntity getSubEntity() {
+      return subEntity;
+    }
+
+    public void setSubEntity(TestSubEntity subEntity) {
+      this.subEntity = subEntity;
+    }
+  }
+
+  public static class TestSubEntity {
+
+    public TestSubEntity() {
+    }
+
+    public TestSubEntity(String name) {
+      this.name = name;
+    }
+
+    String name;
+
+    public String getName() {
+      return name;
+    }
+
+    public void setName(String name) {
+      this.name = name;
+    }
   }
 
   private static class TestModule implements Module, SchemaManagerFactory {