/*
* Copyright 2005 the original author or authors.
- *
+ *
* Licensed 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.wamblee.persistence.hibernate;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.orm.hibernate3.HibernateTemplate;
+import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
+
+import org.wamblee.persistence.Persistent;
+
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.orm.hibernate3.HibernateTemplate;
-import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
-import org.wamblee.persistence.Persistent;
/**
- * Extension of
- * {@link org.springframework.orm.hibernate.support.HibernateDaoSupport}.
+ * Extension of {@link
+ * org.springframework.orm.hibernate.support.HibernateDaoSupport}.
*
* @author Erik Brakkee
*/
public class HibernateSupport extends HibernateDaoSupport {
-
- private static final Log LOG = LogFactory.getLog(HibernateSupport.class);
-
/**
- * This class provided an equality operation based on the object reference
- * of the wrapped object. This is required because we cannto assume that the
- * equals operation has any meaning for different types of persistent
- * objects. This allows us to use the standard collection classes for
- * detecting cyclic dependences and avoiding recursion.
- *
+ * DOCUMENT ME!
*/
- private static final class ObjectElem {
- private Object _object;
-
- public ObjectElem(Object aObject) {
- _object = aObject;
- }
-
- public boolean equals(Object aObj) {
- return ((ObjectElem) aObj)._object == _object;
- }
-
- public int hashCode() {
- return _object.hashCode();
- }
- }
+ private static final Log LOG = LogFactory.getLog(HibernateSupport.class);
- /**
+/**
* Constructs the object.
*
*/
}
/**
- * Performes a hibernate <code>Session.merge()</code> and updates the
- * object with the correct primary key and version. This is an extension to
- * the hibernate merge operation because hibernate itself leaves the object
- * passed to merge untouched.
- *
- * Use this method with extreme caution since it will recursively load all
- * objects that the current object has relations with and for which
- * cascade="merge" was specified in the Hibernate mapping file.
- *
- * @param aPersistent
- * Object to merge.
+ * Performes a hibernate <code>Session.merge()</code> and updates
+ * the object with the correct primary key and version. This is an
+ * extension to the hibernate merge operation because hibernate itself
+ * leaves the object passed to merge untouched. Use this method with
+ * extreme caution since it will recursively load all objects that the
+ * current object has relations with and for which cascade="merge" was
+ * specified in the Hibernate mapping file.
+ *
+ * @param aPersistent Object to merge.
*/
public void merge(Persistent aPersistent) {
merge(getHibernateTemplate(), aPersistent);
}
/**
- * As {@link #merge(Persistent)} but with a given template. This method can
- * be accessed in a static way.
- *
- * @param aTemplate
- * Hibernate template
- * @param aPersistent
- * Object to merge.
+ * As {@link #merge(Persistent)} but with a given template. This
+ * method can be accessed in a static way.
+ *
+ * @param aTemplate Hibernate template
+ * @param aPersistent Object to merge.
*/
public static void merge(HibernateTemplate aTemplate, Persistent aPersistent) {
Persistent merged = (Persistent) aTemplate.merge(aPersistent);
}
/**
- * Copies primary keys and version from the result of the merged to the
- * object that was passed to the merge operation. It does this by traversing
- * the properties of the object. It copies the primary key and version for
- * objects that implement {@link Persistent} and applies the same rules to
- * objects in maps and sets as well (i.e. recursively).
- *
- * @param aPersistent
- * Object whose primary key and version are to be set.
- * @param aMerged
- * Object that was the result of the merge.
- * @param aProcessed
- * List of already processed Persistent objects of the persistent
- * part.
+ * Copies primary keys and version from the result of the merged to
+ * the object that was passed to the merge operation. It does this by
+ * traversing the properties of the object. It copies the primary key and
+ * version for objects that implement {@link Persistent} and applies the
+ * same rules to objects in maps and sets as well (i.e. recursively).
+ *
+ * @param aPersistent Object whose primary key and version are to be set.
+ * @param aMerged Object that was the result of the merge.
+ * @param aProcessed List of already processed Persistent objects of the
+ * persistent part.
+ *
+ * @throws RuntimeException DOCUMENT ME!
*/
public static void processPersistent(Persistent aPersistent,
- Persistent aMerged, List<ObjectElem> aProcessed) {
- if (aPersistent == null && aMerged == null) {
+ Persistent aMerged, List<ObjectElem> aProcessed) {
+ if ((aPersistent == null) && (aMerged == null)) {
return;
}
- if (aPersistent == null || aMerged == null) {
+
+ if ((aPersistent == null) || (aMerged == null)) {
throw new RuntimeException("persistent or merged object is null '"
- + aPersistent + "'" + " '" + aMerged + "'");
+ + aPersistent + "'" + " '" + aMerged + "'");
}
+
ObjectElem elem = new ObjectElem(aPersistent);
+
if (aProcessed.contains(elem)) {
return; // already processed.
}
+
aProcessed.add(elem);
LOG.debug("Setting pk/version on " + aPersistent + " from " + aMerged);
- if (aPersistent.getPrimaryKey() != null
+ if ((aPersistent.getPrimaryKey() != null)
&& !aMerged.getPrimaryKey().equals(aPersistent.getPrimaryKey())) {
LOG.error("Mismatch between primary key values: " + aPersistent
- + " " + aMerged);
+ + " " + aMerged);
} else {
aPersistent.setPersistedVersion(aMerged.getPersistedVersion());
aPersistent.setPrimaryKey(aMerged.getPrimaryKey());
}
Method[] methods = aPersistent.getClass().getMethods();
+
for (Method getter : methods) {
if (getter.getName().startsWith("get")) {
Class returnType = getter.getReturnType();
try {
if (Set.class.isAssignableFrom(returnType)) {
- Set merged = (Set) getter.invoke(aMerged);
+ Set merged = (Set) getter.invoke(aMerged);
Set persistent = (Set) getter.invoke(aPersistent);
processSet(persistent, merged, aProcessed);
} else if (List.class.isAssignableFrom(returnType)) {
- List merged = (List) getter.invoke(aMerged);
+ List merged = (List) getter.invoke(aMerged);
List persistent = (List) getter.invoke(aPersistent);
processList(persistent, merged, aProcessed);
} else if (Map.class.isAssignableFrom(returnType)) {
- Map merged = (Map) getter.invoke(aMerged);
+ Map merged = (Map) getter.invoke(aMerged);
Map persistent = (Map) getter.invoke(aPersistent);
processMap(persistent, merged, aProcessed);
} else if (Persistent.class.isAssignableFrom(returnType)) {
- Persistent merged = (Persistent) getter.invoke(aMerged);
- Persistent persistent = (Persistent) getter
- .invoke(aPersistent);
+ Persistent merged = (Persistent) getter.invoke(aMerged);
+ Persistent persistent = (Persistent) getter.invoke(aPersistent);
processPersistent(persistent, merged, aProcessed);
} else if (returnType.isArray()
- && Persistent.class.isAssignableFrom(returnType
- .getComponentType())) {
- Persistent[] merged = (Persistent[]) getter
- .invoke(aMerged);
- Persistent[] persistent = (Persistent[]) getter
- .invoke(aPersistent);
+ && Persistent.class.isAssignableFrom(
+ returnType.getComponentType())) {
+ Persistent[] merged = (Persistent[]) getter.invoke(aMerged);
+ Persistent[] persistent = (Persistent[]) getter.invoke(aPersistent);
+
for (int i = 0; i < persistent.length; i++) {
processPersistent(persistent[i], merged[i],
- aProcessed);
+ aProcessed);
}
}
} catch (InvocationTargetException e) {
}
}
}
-
}
/**
* Process the persistent objects in the collections.
- *
- * @param aPersistent
- * Collection in the original object.
- * @param aMerged
- * Collection as a result of the merge.
- * @param aProcessed
- * List of processed persistent objects.
+ *
+ * @param aPersistent Collection in the original object.
+ * @param aMerged Collection as a result of the merge.
+ * @param aProcessed List of processed persistent objects.
+ *
+ * @throws RuntimeException DOCUMENT ME!
*/
public static void processList(List aPersistent, List aMerged,
- List<ObjectElem> aProcessed) {
- Object[] merged = aMerged.toArray();
+ List<ObjectElem> aProcessed) {
+ Object[] merged = aMerged.toArray();
Object[] persistent = aPersistent.toArray();
+
if (merged.length != persistent.length) {
throw new RuntimeException("Array sizes differ " + merged.length
- + " " + persistent.length);
+ + " " + persistent.length);
}
+
for (int i = 0; i < merged.length; i++) {
assert merged[i].equals(persistent[i]);
+
if (merged[i] instanceof Persistent) {
processPersistent((Persistent) persistent[i],
- (Persistent) merged[i], aProcessed);
+ (Persistent) merged[i], aProcessed);
}
}
}
/**
* Process the persistent objects in sets.
- *
- * @param aPersistent
- * Collection in the original object.
- * @param aMerged
- * Collection as a result of the merge.
- * @param aProcessed
- * List of processed persistent objects.
+ *
+ * @param aPersistent Collection in the original object.
+ * @param aMerged Collection as a result of the merge.
+ * @param aProcessed List of processed persistent objects.
+ *
+ * @throws RuntimeException DOCUMENT ME!
*/
public static void processSet(Set aPersistent, Set aMerged,
- List<ObjectElem> aProcessed) {
+ List<ObjectElem> aProcessed) {
if (aMerged.size() != aPersistent.size()) {
throw new RuntimeException("Array sizes differ " + aMerged.size()
- + " " + aPersistent.size());
+ + " " + aPersistent.size());
}
+
for (Object merged : aMerged) {
// Find the object that equals the merged[i]
for (Object persistent : aPersistent) {
if (persistent.equals(merged)) {
processPersistent((Persistent) persistent,
- (Persistent) merged, aProcessed);
+ (Persistent) merged, aProcessed);
+
break;
}
}
/**
* Process the Map objects in the collections.
- *
- * @param aPersistent
- * Collection in the original object.
- * @param aMerged
- * Collection as a result of the merge.
- * @param aProcessed
- * List of processed persistent objects.
+ *
+ * @param aPersistent Collection in the original object.
+ * @param aMerged Collection as a result of the merge.
+ * @param aProcessed List of processed persistent objects.
+ *
+ * @throws RuntimeException DOCUMENT ME!
*/
public static void processMap(Map aPersistent, Map aMerged,
- List<ObjectElem> aProcessed) {
+ List<ObjectElem> aProcessed) {
if (aMerged.size() != aPersistent.size()) {
throw new RuntimeException("Sizes differ " + aMerged.size() + " "
- + aPersistent.size());
+ + aPersistent.size());
}
+
Set keys = aMerged.keySet();
+
for (Object key : keys) {
if (!aPersistent.containsKey(key)) {
throw new RuntimeException("Key '" + key + "' not found");
}
- Object mergedValue = aMerged.get(key);
+
+ Object mergedValue = aMerged.get(key);
Object persistentValue = aPersistent.get(key);
+
if (mergedValue instanceof Persistent) {
if (persistentValue instanceof Persistent) {
processPersistent((Persistent) persistentValue,
- (Persistent) mergedValue, aProcessed);
+ (Persistent) mergedValue, aProcessed);
} else {
throw new RuntimeException(
- "Value in original object is null, whereas merged object contains a value");
+ "Value in original object is null, whereas merged object contains a value");
}
}
}
}
+ /**
+ * This class provided an equality operation based on the object
+ * reference of the wrapped object. This is required because we cannto
+ * assume that the equals operation has any meaning for different types of
+ * persistent objects. This allows us to use the standard collection
+ * classes for detecting cyclic dependences and avoiding recursion.
+ */
+ private static final class ObjectElem {
+ private Object object;
+
+ public ObjectElem(Object aObject) {
+ object = aObject;
+ }
+
+ public boolean equals(Object aObj) {
+ return ((ObjectElem) aObj).object == object;
+ }
+
+ public int hashCode() {
+ return object.hashCode();
+ }
+ }
}