You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/08/11 10:46:34 UTC
cayenne git commit: CAY-2346 Field-based data object with Map-based
storage fallback
Repository: cayenne
Updated Branches:
refs/heads/master 7b762e9a3 -> 7f278e001
CAY-2346 Field-based data object with Map-based storage fallback
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/7f278e00
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/7f278e00
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/7f278e00
Branch: refs/heads/master
Commit: 7f278e001c68a8588658e0071b418c07b304c66c
Parents: 7b762e9
Author: Nikita Timofeev <st...@gmail.com>
Authored: Fri Aug 11 13:13:26 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Fri Aug 11 13:13:26 2017 +0300
----------------------------------------------------------------------
.../java/org/apache/cayenne/BaseDataObject.java | 22 +-
.../org/apache/cayenne/CayenneDataObject.java | 12 +-
.../org/apache/cayenne/HybridDataObject.java | 130 ++++++++++++
.../org/apache/cayenne/HybridDataObjectIT.java | 207 +++++++++++++++++++
.../cayenne/testdo/hybrid/HybridEntity1.java | 9 +
.../cayenne/testdo/hybrid/HybridEntity2.java | 9 +
.../testdo/hybrid/auto/_HybridEntity1.java | 129 ++++++++++++
.../testdo/hybrid/auto/_HybridEntity2.java | 123 +++++++++++
.../cayenne/unit/di/server/CayenneProjects.java | 1 +
.../cayenne/unit/di/server/SchemaBuilder.java | 2 +-
.../resources/cayenne-hybrid-data-object.xml | 8 +
.../test/resources/hybrid-data-object.map.xml | 37 ++++
12 files changed, 682 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/main/java/org/apache/cayenne/BaseDataObject.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/BaseDataObject.java b/cayenne-server/src/main/java/org/apache/cayenne/BaseDataObject.java
index 17447c7..a12b1bb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/BaseDataObject.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/BaseDataObject.java
@@ -43,14 +43,26 @@ import org.apache.cayenne.validation.ValidationFailure;
import org.apache.cayenne.validation.ValidationResult;
/**
- * Base implementation of DataObject,
+ * Base implementation of {@link DataObject},
* have no assumption about how data is actually stored.
- *
- * Two variants are currently supported:
- * - field based storage, e.g. each entity class will directly define fields to store data
- * - Map based storage, e.g. values will be stored in general Map (see {@link CayenneDataObject})
+ * <p>
+ * Three variants are currently supported:
+ * <ul>
+ * <li> field based storage, e.g. each entity class will directly define fields to store data
+ * <li> {@link Map} based storage, e.g. values will be stored in general Map ({@link CayenneDataObject})
+ * <li> mixed fields and generic Map to store runtime attributes ({@link HybridDataObject})
+ * </ul>
+ * <p>
+ * This class can be used directly as superclass for field-based data objects.
+ * <p>
+ * To create own implementation of {@link DataObject} with custom field storage logic it is enough
+ * to implement {@link #readPropertyDirectly(String)} and {@link #writePropertyDirectly(String, Object)} methods
+ * and serialization support if needed (helper methods {@link #writeState(ObjectOutputStream)}
+ * and {@link #readState(ObjectInputStream)} are provided).
*
* @see CayenneDataObject
+ * @see HybridDataObject
+ *
* @since 4.1
*/
public abstract class BaseDataObject extends PersistentObject implements DataObject, Validating {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/main/java/org/apache/cayenne/CayenneDataObject.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/CayenneDataObject.java b/cayenne-server/src/main/java/org/apache/cayenne/CayenneDataObject.java
index 41e18ff..3cad849 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/CayenneDataObject.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/CayenneDataObject.java
@@ -30,8 +30,18 @@ import java.util.Iterator;
import java.util.Map;
/**
- * Implementation of DataObject that uses {@link Map} to store object fields.
+ * Implementation of {@link DataObject} that uses {@link Map} to store object fields.
+ * <p>
* This implementation was pre 4.1 default.
+ * <p>
+ * Since <b>4.1</b> it is recommended to use {@link BaseDataObject} as superclass (and it is actually default now),
+ * as it has better performance and lower memory consumption (<b>much</b> lower for small objects).
+ * <p>
+ * You may need to use this class only if you have some generic attributes created at runtime (also
+ * consider {@link HybridDataObject} in this case) or if any compatibility issues arise.
+ *
+ * @see BaseDataObject
+ * @see HybridDataObject
*/
public class CayenneDataObject extends BaseDataObject {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/main/java/org/apache/cayenne/HybridDataObject.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/HybridDataObject.java b/cayenne-server/src/main/java/org/apache/cayenne/HybridDataObject.java
new file mode 100644
index 0000000..a998c76
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/HybridDataObject.java
@@ -0,0 +1,130 @@
+/*****************************************************************
+ * 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;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.cayenne.reflect.PropertyUtils;
+
+/**
+ *
+ * This data object like {@link CayenneDataObject} uses {@link Map} to store generic attributes,
+ * only difference is that this Map will be created lazily at first write, thus reducing memory penalty if possible.
+ * <p>
+ * This class can be used as superclass for objects that have attributes created at runtime.
+ * If generic runtime attributes will be used always it may be a good idea to use {@link CayenneDataObject} instead.
+ * If you don't create attributes at runtime it is better to use {@link BaseDataObject} class.
+ * <p>
+ * Map creation is not thread safe, as DataObject in general not thread safe by it's own.
+ *
+ * @see BaseDataObject
+ * @see CayenneDataObject
+ *
+ * @since 4.1
+ */
+public class HybridDataObject extends BaseDataObject {
+
+ private static final long serialVersionUID = 1945209973678806566L;
+
+ protected Map<String, Object> values;
+
+ @Override
+ Object readSimpleProperty(String property) {
+
+ // side effect - resolves HOLLOW object
+ Object object = readProperty(property);
+
+ // if a null value is returned, there is still a chance to
+ // find a non-persistent property via reflection
+ if (object == null && values != null && !values.containsKey(property)) {
+ object = PropertyUtils.getProperty(this, property);
+ }
+
+ return object;
+ }
+
+ @Override
+ public Object readPropertyDirectly(String propName) {
+ if(values == null) {
+ return null;
+ }
+ return values.get(propName);
+ }
+
+ @Override
+ public void writePropertyDirectly(String propName, Object val) {
+ if(values == null) {
+ values = new HashMap<>();
+ }
+ values.put(propName, val);
+ }
+
+ @Override
+ protected void appendProperties(StringBuffer buffer) {
+ buffer.append('[');
+ if(values == null) {
+ buffer.append(']');
+ return;
+ }
+
+ Iterator<Map.Entry<String, Object>> it = values.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, Object> entry = it.next();
+
+ buffer.append(entry.getKey()).append("=>");
+ Object value = entry.getValue();
+
+ if (value instanceof Persistent) {
+ buffer.append('{').append(((Persistent) value).getObjectId()).append('}');
+ } else if (value instanceof Collection) {
+ buffer.append("(..)");
+ } else if (value instanceof Fault) {
+ buffer.append('?');
+ } else {
+ buffer.append(value);
+ }
+
+ if (it.hasNext()) {
+ buffer.append("; ");
+ }
+ }
+
+ buffer.append(']');
+ }
+
+ @Override
+ protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ super.readState(in);
+ values = (Map<String, Object>) in.readObject();
+ }
+
+ @Override
+ protected void writeState(ObjectOutputStream out) throws IOException {
+ super.writeState(out);
+ out.writeObject(values);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/java/org/apache/cayenne/HybridDataObjectIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/HybridDataObjectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/HybridDataObjectIT.java
new file mode 100644
index 0000000..e25751e
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/HybridDataObjectIT.java
@@ -0,0 +1,207 @@
+/*****************************************************************
+ * 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;
+
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.testdo.hybrid.HybridEntity1;
+import org.apache.cayenne.testdo.hybrid.HybridEntity2;
+import org.apache.cayenne.unit.di.server.CayenneProjects;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+import org.apache.cayenne.util.Util;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+/**
+ * @since 4.1
+ */
+@UseServerRuntime(CayenneProjects.HYBRID_DATA_OBJECT_PROJECT)
+public class HybridDataObjectIT extends ServerCase {
+
+ @Inject
+ private DataContext context;
+
+ @Inject
+ private ServerRuntime runtime;
+
+ @Test
+ public void testCreateNew() {
+ HybridEntity1 entity1 = context.newObject(HybridEntity1.class);
+ HybridEntity2 entity2 = context.newObject(HybridEntity2.class);
+ context.commitChanges();
+
+ assertNull(entity1.values);
+ assertNull(entity2.values);
+
+ HybridEntity1 selectEntity1 = ObjectSelect.query(HybridEntity1.class).selectOne(context);
+ assertEquals(0, selectEntity1.getIntField());
+ assertEquals(null, selectEntity1.getStrField());
+
+ HybridEntity2 selectEntity2 = ObjectSelect.query(HybridEntity2.class).selectOne(context);
+ assertEquals(0, selectEntity2.getIntField());
+ assertEquals(null, selectEntity2.getStrField());
+ }
+
+ @Test
+ public void testSetFieldAttributes() {
+ HybridEntity1 entity1 = context.newObject(HybridEntity1.class);
+ entity1.setIntField(123);
+ entity1.setStrField("abc");
+
+ HybridEntity2 entity2 = context.newObject(HybridEntity2.class);
+ entity2.setIntField(321);
+ entity2.setStrField("cba");
+ entity2.setHybridEntity1(entity1);
+
+ assertNull(entity1.values);
+ assertNull(entity2.values);
+
+ context.commitChanges();
+
+ HybridEntity1 selectEntity1 = ObjectSelect.query(HybridEntity1.class).selectOne(context);
+ assertEquals(123, selectEntity1.getIntField());
+ assertEquals("abc", selectEntity1.getStrField());
+
+ HybridEntity2 selectEntity2 = ObjectSelect.query(HybridEntity2.class).selectOne(context);
+ assertEquals(321, selectEntity2.getIntField());
+ assertEquals("cba", selectEntity2.getStrField());
+ assertEquals(selectEntity1, selectEntity2.getHybridEntity1());
+ }
+
+ @Test
+ public void testSetDynamicDbAttributes() {
+ // add attributes that in DbEntity but not mapped yet
+ addRuntimeAttribute(HybridEntity1.class, "FLOAT_FIELD", "double");
+ addRuntimeAttribute(HybridEntity2.class, "BOOLEAN_FIELD", "boolean");
+
+ try {
+ HybridEntity1 entity1 = context.newObject(HybridEntity1.class);
+ entity1.writeProperty("FLOAT_FIELD", 3.14);
+
+ HybridEntity2 entity2 = context.newObject(HybridEntity2.class);
+ entity2.writeProperty("BOOLEAN_FIELD", true);
+
+ assertNotNull(entity1.values);
+ assertNotNull(entity2.values);
+
+ context.commitChanges();
+
+ entity1.writeProperty("FLOAT_FIELD", 2.17);
+ entity2.writeProperty("BOOLEAN_FIELD", false);
+
+ // attributes should be merged with context cache
+ HybridEntity1 selectEntity1 = ObjectSelect.query(HybridEntity1.class).selectOne(context);
+ assertEquals(2.17, selectEntity1.readProperty("FLOAT_FIELD"));
+
+ HybridEntity2 selectEntity2 = ObjectSelect.query(HybridEntity2.class).selectOne(context);
+ assertEquals(false, selectEntity2.readProperty("BOOLEAN_FIELD"));
+
+ // attributes should be read from DB
+ ObjectContext cleanContext = runtime.newContext();
+ HybridEntity1 selectCleanEntity1 = ObjectSelect.query(HybridEntity1.class).selectOne(cleanContext);
+ assertEquals(3.14, selectCleanEntity1.readProperty("FLOAT_FIELD"));
+
+ HybridEntity2 selectCleanEntity2 = ObjectSelect.query(HybridEntity2.class).selectOne(cleanContext);
+ assertEquals(true, selectCleanEntity2.readProperty("BOOLEAN_FIELD"));
+ } finally {
+ removeRuntimeAttribute(HybridEntity1.class, "FLOAT_FIELD");
+ removeRuntimeAttribute(HybridEntity2.class, "BOOLEAN_FIELD");
+ }
+ }
+
+ @Test
+ public void testSetDynamicNonDbAttributes() {
+ // test write arbitrary data into object
+ HybridEntity1 entity1 = context.newObject(HybridEntity1.class);
+ entity1.writeProperty("CUSTOM_NON_DB_ATTRIBUTE", 42L);
+ assertEquals(42L, entity1.readProperty("CUSTOM_NON_DB_ATTRIBUTE"));
+ assertNotNull(entity1.values);
+
+ context.commitChanges();
+
+ entity1.writeProperty("CUSTOM_NON_DB_ATTRIBUTE", 12L);
+
+ HybridEntity1 selectEntity1 = ObjectSelect.query(HybridEntity1.class).selectOne(context);
+ // this will be restored from context cache
+ assertEquals(12L, selectEntity1.readProperty("CUSTOM_NON_DB_ATTRIBUTE"));
+
+ ObjectContext cleanContext = runtime.newContext();
+
+ HybridEntity1 selectCleanEntity1 = ObjectSelect.query(HybridEntity1.class).selectOne(cleanContext);
+ // this will be read from db only
+ assertEquals(null, selectCleanEntity1.readProperty("CUSTOM_NON_DB_ATTRIBUTE"));
+ }
+
+ @Test
+ public void testSerialization() throws Exception {
+
+ HybridEntity1 entity1 = new HybridEntity1();
+ entity1.setIntField(123);
+ entity1.setStrField("abc");
+ entity1.writeProperty("CUSTOM_PROPERTY", 3.14);
+
+ HybridEntity1 clonedEntity1 = Util.cloneViaSerialization(entity1);
+
+ assertEquals(123, clonedEntity1.getIntField());
+ assertEquals("abc", clonedEntity1.getStrField());
+ assertEquals(3.14, clonedEntity1.readProperty("CUSTOM_PROPERTY"));
+ }
+
+ @Test
+ public void testDirectPropertyWrite() throws Exception {
+ HybridEntity1 entity1 = new HybridEntity1();
+
+ HybridEntity2 entity2 = new HybridEntity2();
+ entity2.writePropertyDirectly("intField", 123);
+ entity2.writePropertyDirectly("strField", "abc");
+ assertNull(entity2.values);
+
+ entity2.writePropertyDirectly("CUSTOM_PROPERTY", 3.14);
+ entity2.writePropertyDirectly("hybridEntity1", entity1);
+ assertNotNull(entity2.values);
+
+ assertEquals(123, entity2.readPropertyDirectly("intField"));
+ assertEquals("abc", entity2.readPropertyDirectly("strField"));
+ assertEquals(3.14, entity2.readPropertyDirectly("CUSTOM_PROPERTY"));
+ assertEquals(entity1, entity2.readPropertyDirectly("hybridEntity1"));
+ }
+
+ private void addRuntimeAttribute(Class<?> entityClass, String attributeName, String attributeType) {
+ ObjEntity entity = runtime.getDataDomain().getEntityResolver().getObjEntity(entityClass);
+ ObjAttribute attribute = new ObjAttribute();
+ attribute.setName(attributeName);
+ attribute.setDbAttributePath(attributeName);
+ attribute.setType(attributeType);
+ entity.addAttribute(attribute);
+ }
+
+ private void removeRuntimeAttribute(Class<?> entityClass, String attributeName) {
+ ObjEntity entity = runtime.getDataDomain().getEntityResolver().getObjEntity(entityClass);
+ entity.removeAttribute(attributeName);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity1.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity1.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity1.java
new file mode 100644
index 0000000..e7767fe
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity1.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.testdo.hybrid;
+
+import org.apache.cayenne.testdo.hybrid.auto._HybridEntity1;
+
+public class HybridEntity1 extends _HybridEntity1 {
+
+ private static final long serialVersionUID = 1L;
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity2.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity2.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity2.java
new file mode 100644
index 0000000..b2cf3f4
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/HybridEntity2.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.testdo.hybrid;
+
+import org.apache.cayenne.testdo.hybrid.auto._HybridEntity2;
+
+public class HybridEntity2 extends _HybridEntity2 {
+
+ private static final long serialVersionUID = 1L;
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity1.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity1.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity1.java
new file mode 100644
index 0000000..82aa0e8
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity1.java
@@ -0,0 +1,129 @@
+package org.apache.cayenne.testdo.hybrid.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.List;
+
+import org.apache.cayenne.HybridDataObject;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.testdo.hybrid.HybridEntity2;
+
+/**
+ * Class _HybridEntity1 was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _HybridEntity1 extends HybridDataObject {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String ID_PK_COLUMN = "ID";
+
+ public static final Property<Integer> INT_FIELD = Property.create("intField", Integer.class);
+ public static final Property<String> STR_FIELD = Property.create("strField", String.class);
+ public static final Property<List<HybridEntity2>> HYBRID_ENTITIES2 = Property.create("hybridEntities2", List.class);
+
+ protected int intField;
+ protected String strField;
+
+ protected Object hybridEntities2;
+
+ public void setIntField(int intField) {
+ beforePropertyWrite("intField", this.intField, intField);
+ this.intField = intField;
+ }
+
+ public int getIntField() {
+ beforePropertyRead("intField");
+ return this.intField;
+ }
+
+ public void setStrField(String strField) {
+ beforePropertyWrite("strField", this.strField, strField);
+ this.strField = strField;
+ }
+
+ public String getStrField() {
+ beforePropertyRead("strField");
+ return this.strField;
+ }
+
+ public void addToHybridEntities2(HybridEntity2 obj) {
+ addToManyTarget("hybridEntities2", obj, true);
+ }
+
+ public void removeFromHybridEntities2(HybridEntity2 obj) {
+ removeToManyTarget("hybridEntities2", obj, true);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<HybridEntity2> getHybridEntities2() {
+ return (List<HybridEntity2>)readProperty("hybridEntities2");
+ }
+
+ @Override
+ public Object readPropertyDirectly(String propName) {
+ if(propName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ switch(propName) {
+ case "intField":
+ return this.intField;
+ case "strField":
+ return this.strField;
+ case "hybridEntities2":
+ return this.hybridEntities2;
+ default:
+ return super.readPropertyDirectly(propName);
+ }
+ }
+
+ @Override
+ public void writePropertyDirectly(String propName, Object val) {
+ if(propName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ switch (propName) {
+ case "intField":
+ this.intField = val == null ? 0 : (Integer)val;
+ break;
+ case "strField":
+ this.strField = (String)val;
+ break;
+ case "hybridEntities2":
+ this.hybridEntities2 = val;
+ break;
+ default:
+ super.writePropertyDirectly(propName, val);
+ }
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ writeSerialized(out);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ readSerialized(in);
+ }
+
+ @Override
+ protected void writeState(ObjectOutputStream out) throws IOException {
+ super.writeState(out);
+ out.writeInt(this.intField);
+ out.writeObject(this.strField);
+ out.writeObject(this.hybridEntities2);
+ }
+
+ @Override
+ protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ super.readState(in);
+ this.intField = in.readInt();
+ this.strField = (String)in.readObject();
+ this.hybridEntities2 = in.readObject();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity2.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity2.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity2.java
new file mode 100644
index 0000000..c923af3
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/hybrid/auto/_HybridEntity2.java
@@ -0,0 +1,123 @@
+package org.apache.cayenne.testdo.hybrid.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.cayenne.HybridDataObject;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.testdo.hybrid.HybridEntity1;
+
+/**
+ * Class _HybridEntity2 was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _HybridEntity2 extends HybridDataObject {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String ID_PK_COLUMN = "ID";
+
+ public static final Property<Integer> INT_FIELD = Property.create("intField", Integer.class);
+ public static final Property<String> STR_FIELD = Property.create("strField", String.class);
+ public static final Property<HybridEntity1> HYBRID_ENTITY1 = Property.create("hybridEntity1", HybridEntity1.class);
+
+ protected int intField;
+ protected String strField;
+
+ protected Object hybridEntity1;
+
+ public void setIntField(int intField) {
+ beforePropertyWrite("intField", this.intField, intField);
+ this.intField = intField;
+ }
+
+ public int getIntField() {
+ beforePropertyRead("intField");
+ return this.intField;
+ }
+
+ public void setStrField(String strField) {
+ beforePropertyWrite("strField", this.strField, strField);
+ this.strField = strField;
+ }
+
+ public String getStrField() {
+ beforePropertyRead("strField");
+ return this.strField;
+ }
+
+ public void setHybridEntity1(HybridEntity1 hybridEntity1) {
+ setToOneTarget("hybridEntity1", hybridEntity1, true);
+ }
+
+ public HybridEntity1 getHybridEntity1() {
+ return (HybridEntity1)readProperty("hybridEntity1");
+ }
+
+ @Override
+ public Object readPropertyDirectly(String propName) {
+ if(propName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ switch(propName) {
+ case "intField":
+ return this.intField;
+ case "strField":
+ return this.strField;
+ case "hybridEntity1":
+ return this.hybridEntity1;
+ default:
+ return super.readPropertyDirectly(propName);
+ }
+ }
+
+ @Override
+ public void writePropertyDirectly(String propName, Object val) {
+ if(propName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ switch (propName) {
+ case "intField":
+ this.intField = val == null ? 0 : (Integer)val;
+ break;
+ case "strField":
+ this.strField = (String)val;
+ break;
+ case "hybridEntity1":
+ this.hybridEntity1 = val;
+ break;
+ default:
+ super.writePropertyDirectly(propName, val);
+ }
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ writeSerialized(out);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ readSerialized(in);
+ }
+
+ @Override
+ protected void writeState(ObjectOutputStream out) throws IOException {
+ super.writeState(out);
+ out.writeInt(this.intField);
+ out.writeObject(this.strField);
+ out.writeObject(this.hybridEntity1);
+ }
+
+ @Override
+ protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ super.readState(in);
+ this.intField = in.readInt();
+ this.strField = (String)in.readObject();
+ this.hybridEntity1 = in.readObject();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java
index 0cd6372..10615ab 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/CayenneProjects.java
@@ -80,4 +80,5 @@ public class CayenneProjects {
public static final String UUID_PROJECT = "cayenne-uuid.xml";
public static final String CUSTOM_NAME_PROJECT = "custom-name-file.xml";
public static final String WEIGHTED_SORT_PROJECT = "cayenne-weighted-sort.xml";
+ public static final String HYBRID_DATA_OBJECT_PROJECT = "cayenne-hybrid-data-object.xml";
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java
index 5101bd5..f217052 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/SchemaBuilder.java
@@ -82,7 +82,7 @@ public class SchemaBuilder {
"table-primitives.map.xml", "generic.map.xml", "map-db1.map.xml", "map-db2.map.xml", "embeddable.map.xml",
"qualified.map.xml", "quoted-identifiers.map.xml", "inheritance-single-table1.map.xml",
"inheritance-vertical.map.xml", "oneway-rels.map.xml", "unsupported-distinct-types.map.xml",
- "array-type.map.xml", "cay-2032.map.xml", "weighted-sort.map.xml" };
+ "array-type.map.xml", "cay-2032.map.xml", "weighted-sort.map.xml", "hybrid-data-object.map.xml" };
// hardcoded dependent entities that should be excluded
// if LOBs are not supported
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/resources/cayenne-hybrid-data-object.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/resources/cayenne-hybrid-data-object.xml b/cayenne-server/src/test/resources/cayenne-hybrid-data-object.xml
new file mode 100644
index 0000000..4fc8784
--- /dev/null
+++ b/cayenne-server/src/test/resources/cayenne-hybrid-data-object.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain xmlns="http://cayenne.apache.org/schema/10/domain"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain http://cayenne.apache.org/schema/10/domain.xsd"
+ project-version="10">
+ <property name="cayenne.DataDomain.sharedCache" value="false"/>
+ <map name="hybrid-data-object"/>
+</domain>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/7f278e00/cayenne-server/src/test/resources/hybrid-data-object.map.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/resources/hybrid-data-object.map.xml b/cayenne-server/src/test/resources/hybrid-data-object.map.xml
new file mode 100644
index 0000000..441bf60
--- /dev/null
+++ b/cayenne-server/src/test/resources/hybrid-data-object.map.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<data-map xmlns="http://cayenne.apache.org/schema/10/modelMap"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap http://cayenne.apache.org/schema/10/modelMap.xsd"
+ project-version="10">
+ <property name="defaultPackage" value="org.apache.cayenne.testdo.hybrid"/>
+ <property name="defaultSuperclass" value="org.apache.cayenne.HybridDataObject"/>
+ <db-entity name="HYBRID_ENTITY_1">
+ <db-attribute name="FLOAT_FIELD" type="DOUBLE"/>
+ <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+ <db-attribute name="INT_FIELD" type="INTEGER" isMandatory="true"/>
+ <db-attribute name="STR_FIELD" type="VARCHAR" length="255"/>
+ </db-entity>
+ <db-entity name="HYBRID_ENTITY_2">
+ <db-attribute name="BOOLEAN_FIELD" type="BOOLEAN"/>
+ <db-attribute name="HYBRID_ENTITY_1_ID" type="INTEGER"/>
+ <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+ <db-attribute name="INT_FIELD" type="INTEGER" isMandatory="true"/>
+ <db-attribute name="STR_FIELD" type="VARCHAR" length="255"/>
+ </db-entity>
+ <obj-entity name="HybridEntity1" className="org.apache.cayenne.testdo.hybrid.HybridEntity1" dbEntityName="HYBRID_ENTITY_1" superClassName="org.apache.cayenne.HybridDataObject">
+ <obj-attribute name="intField" type="int" db-attribute-path="INT_FIELD"/>
+ <obj-attribute name="strField" type="java.lang.String" db-attribute-path="STR_FIELD"/>
+ </obj-entity>
+ <obj-entity name="HybridEntity2" className="org.apache.cayenne.testdo.hybrid.HybridEntity2" dbEntityName="HYBRID_ENTITY_2" superClassName="org.apache.cayenne.HybridDataObject">
+ <obj-attribute name="intField" type="int" db-attribute-path="INT_FIELD"/>
+ <obj-attribute name="strField" type="java.lang.String" db-attribute-path="STR_FIELD"/>
+ </obj-entity>
+ <db-relationship name="hybridEntities2" source="HYBRID_ENTITY_1" target="HYBRID_ENTITY_2" toMany="true">
+ <db-attribute-pair source="ID" target="HYBRID_ENTITY_1_ID"/>
+ </db-relationship>
+ <db-relationship name="hybridEntity1" source="HYBRID_ENTITY_2" target="HYBRID_ENTITY_1">
+ <db-attribute-pair source="HYBRID_ENTITY_1_ID" target="ID"/>
+ </db-relationship>
+ <obj-relationship name="hybridEntities2" source="HybridEntity1" target="HybridEntity2" deleteRule="Deny" db-relationship-path="hybridEntities2"/>
+ <obj-relationship name="hybridEntity1" source="HybridEntity2" target="HybridEntity1" deleteRule="Nullify" db-relationship-path="hybridEntity1"/>
+</data-map>