Selaa lähdekoodia

Handle ORM proxy subclass comparison in Entity and ValueObject support classes.

peter_backlund 16 vuotta sitten
vanhempi
commit
ffbd13688f

+ 36
- 0
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/patterns/OrmUtils.java Näytä tiedosto

@@ -0,0 +1,36 @@
1
+package se.citerus.dddsample.tracking.core.domain.patterns;
2
+
3
+import org.hibernate.proxy.AbstractLazyInitializer;
4
+import org.hibernate.proxy.HibernateProxy;
5
+
6
+import java.lang.reflect.Field;
7
+
8
+/**
9
+ * Utils for working with (around) the ORM framework, i.e. Hibernate.
10
+ */
11
+public class OrmUtils {
12
+
13
+  private static final String HANDLER_FIELD_NAME = "handler";
14
+
15
+  /**
16
+   * @param o an object, possibly wrapped in a lazy proxy
17
+   * @return the wrapped persistent entity (if wrapped), or o
18
+   */
19
+  public static Object unwrapOrmProxy(final Object o) {
20
+    if (o instanceof HibernateProxy) {
21
+      try {
22
+        final Field handlerField = o.getClass().getDeclaredField(HANDLER_FIELD_NAME);
23
+        handlerField.setAccessible(true);
24
+        final AbstractLazyInitializer handler = (AbstractLazyInitializer) handlerField.get(o);
25
+        return handler.getImplementation();
26
+      } catch (IllegalAccessException e) {
27
+        throw new AssertionError(e);
28
+      } catch (NoSuchFieldException e) {
29
+        throw new AssertionError(e);
30
+      }
31
+    } else {
32
+      return o;
33
+    }
34
+  }
35
+
36
+}

+ 9
- 8
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/patterns/entity/EntitySupport.java Näytä tiedosto

@@ -1,12 +1,14 @@
1 1
 package se.citerus.dddsample.tracking.core.domain.patterns.entity;
2 2
 
3
+import static se.citerus.dddsample.tracking.core.domain.patterns.OrmUtils.unwrapOrmProxy;
4
+
3 5
 /**
4 6
  * Supporting base class for entities.
5 7
  *
6 8
  * While the Entity interface makes the pattern properties explicit,
7 9
  * this class is less general and is suited for this particular application.
8 10
  * </p>
9
- * For example, the private id field is meant for autogenerated, surrogate primary keys.
11
+ * For example, the private _primaryKey field is meant for autogenerated, numerical surrogate primary keys.
10 12
  */
11 13
 public abstract class EntitySupport<T extends Entity, ID> implements Entity<T, ID> {
12 14
 
@@ -23,15 +25,14 @@ public abstract class EntitySupport<T extends Entity, ID> implements Entity<T, I
23 25
     return identity().hashCode();
24 26
   }
25 27
 
26
-  @SuppressWarnings({"SimplifiableIfStatement", "unchecked"})
28
+  @SuppressWarnings({"SimplifiableIfStatement", "unchecked", "EqualsWhichDoesntCheckParameterClass"})
27 29
   @Override
28
-  public final boolean equals(final Object o) {
29
-    if (this == o) return true;
30
-    // TODO class comparision is too strict for ORM proxies
31
-    if (o == null || !(o instanceof EntitySupport)) return false;
32
-    //if (o == null || getClass() != o.getClass()) return false;
30
+  public final boolean equals(final Object other) {
31
+    if (other == null) return false;
32
+    if (this == other) return true;
33
+    if (unwrapOrmProxy(this).getClass() != unwrapOrmProxy(other).getClass()) return false;
33 34
 
34
-    return sameAs((T) o);
35
+    return sameAs((T) other);
35 36
   }
36 37
 
37 38
 }

+ 11
- 9
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/patterns/valueobject/ValueObjectSupport.java Näytä tiedosto

@@ -2,10 +2,11 @@ package se.citerus.dddsample.tracking.core.domain.patterns.valueobject;
2 2
 
3 3
 import org.apache.commons.lang.builder.EqualsBuilder;
4 4
 import org.apache.commons.lang.builder.HashCodeBuilder;
5
+import static se.citerus.dddsample.tracking.core.domain.patterns.OrmUtils.unwrapOrmProxy;
5 6
 
6 7
 /**
7 8
  * Supporting base class for value objects.
8
- *
9
+ * <p/>
9 10
  * While the ValueObject interface makes the pattern properties explicit,
10 11
  * this class is less general and is suited for this particular application.
11 12
  * </p>
@@ -21,7 +22,7 @@ public abstract class ValueObjectSupport<T extends ValueObject> implements Value
21 22
   @SuppressWarnings("UnusedDeclaration")
22 23
   private final Long _primaryKey = null;
23 24
   private transient int _cachedHashCode = 0;
24
-  private final static String[] EXCLUDED_FIELDS = {"_primaryKey", "_cachedHashCode"};
25
+  private static final String[] EXCLUDED_FIELDS = {"_primaryKey", "_cachedHashCode"};
25 26
 
26 27
   /**
27 28
    * @param other The other value object.
@@ -29,7 +30,7 @@ public abstract class ValueObjectSupport<T extends ValueObject> implements Value
29 30
    */
30 31
   @Override
31 32
   public final boolean sameValueAs(final T other) {
32
-    return other != null && EqualsBuilder.reflectionEquals(this, other, EXCLUDED_FIELDS);
33
+    return other != null && EqualsBuilder.reflectionEquals(unwrapOrmProxy(this), unwrapOrmProxy(other), EXCLUDED_FIELDS);
33 34
   }
34 35
 
35 36
   /**
@@ -56,16 +57,17 @@ public abstract class ValueObjectSupport<T extends ValueObject> implements Value
56 57
   }
57 58
 
58 59
   /**
59
-   * @param o other object
60
+   * @param other other object
60 61
    * @return True if other object has the same value as this value object.
61 62
    */
62
-  @SuppressWarnings({"SimplifiableIfStatement", "unchecked"})
63
+  @SuppressWarnings({"SimplifiableIfStatement", "unchecked", "EqualsWhichDoesntCheckParameterClass"})
63 64
   @Override
64
-  public final boolean equals(final Object o) {
65
-    if (this == o) return true;
66
-    if (o == null || getClass() != o.getClass()) return false;
65
+  public final boolean equals(final Object other) {
66
+    if (other == null) return false;
67
+    if (this == other) return true;
68
+    if (unwrapOrmProxy(this).getClass() != unwrapOrmProxy(other).getClass()) return false;
67 69
 
68
-    return sameValueAs((T) o);
70
+    return sameValueAs((T) other);
69 71
   }
70 72
 
71 73
 }