This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit f5ab0eb4e5e49f085ca1aeaa0d88b31fd9e9ddc7
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sat May 23 18:27:06 2026 -0400

    Cleanup:  HashCodeBuilder is deprecated in favor of plain Java alternatives
---
 .../cayenne/dbsync/reverse/dbload/ExportedKey.java |  15 +-
 .../java/org/apache/cayenne/ObjectIdCompound.java  | 428 ++++++++++-----------
 .../java/org/apache/cayenne/ObjectIdSingle.java    |  28 +-
 .../cayenne/access/jdbc/ColumnDescriptor.java      |   4 +-
 .../org/apache/cayenne/event/EventSubject.java     |   6 +-
 .../java/org/apache/cayenne/exp/Expression.java    |   9 +-
 .../org/apache/cayenne/util/HashCodeBuilder.java   |   4 +-
 .../main/java/org/apache/cayenne/util/Util.java    |   6 +-
 8 files changed, 244 insertions(+), 256 deletions(-)

diff --git 
a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/ExportedKey.java
 
b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/ExportedKey.java
index ba06ad8c9..374c36f1d 100644
--- 
a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/ExportedKey.java
+++ 
b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/ExportedKey.java
@@ -25,7 +25,6 @@ import java.util.Objects;
 
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.util.CompareToBuilder;
-import org.apache.cayenne.util.HashCodeBuilder;
 import org.apache.cayenne.util.Util;
 
 /**
@@ -92,11 +91,7 @@ public class ExportedKey implements Comparable<ExportedKey> {
 
     @Override
     public int hashCode() {
-        return new HashCodeBuilder()
-                .append(pk)
-                .append(fk)
-                .append(keySeq)
-                .toHashCode();
+        return Objects.hash(pk, fk, keySeq);
     }
 
     @Override
@@ -198,13 +193,7 @@ public class ExportedKey implements 
Comparable<ExportedKey> {
 
         @Override
         public int hashCode() {
-            return new HashCodeBuilder()
-                    .append(catalog)
-                    .append(schema)
-                    .append(table)
-                    .append(column)
-                    .append(name)
-                    .toHashCode();
+            return Objects.hash(catalog, schema, table, column, name);
         }
 
         /**
diff --git a/cayenne/src/main/java/org/apache/cayenne/ObjectIdCompound.java 
b/cayenne/src/main/java/org/apache/cayenne/ObjectIdCompound.java
index 15614437a..d124a0c0c 100644
--- a/cayenne/src/main/java/org/apache/cayenne/ObjectIdCompound.java
+++ b/cayenne/src/main/java/org/apache/cayenne/ObjectIdCompound.java
@@ -19,7 +19,6 @@
 
 package org.apache.cayenne;
 
-import org.apache.cayenne.util.HashCodeBuilder;
 import java.util.Objects;
 
 import java.util.Arrays;
@@ -29,227 +28,216 @@ import java.util.Map;
 
 /**
  * Compound {@link ObjectId}
+ *
  * @since 4.2
  */
 class ObjectIdCompound implements ObjectId {
 
-       private static final long serialVersionUID = -2265029098344119323L;
-       
-       protected final String entityName;
-       protected final Map<String, Object> objectIdKeys;
-
-       protected Map<String, Object> replacementIdMap;
-
-       // hash code is transient to make sure id is portable across VM
-       private transient int hashCode;
-
-       // exists for deserialization with Hessian and similar
-       @SuppressWarnings("unused")
-       private ObjectIdCompound() {
-               entityName = null;
-               objectIdKeys = Collections.emptyMap();
-       }
-
-       /**
-        * Creates a portable permanent ObjectId as a compound primary key.
-        * 
-        * @param entityName
-        *            The entity name which this object id is for
-        * @param idMap
-        *            Keys are usually the attribute names for each part of the
-        *            primary key. Values are unique when taken as a whole.
-        * @since 1.2
-        */
-       ObjectIdCompound(String entityName, Map<String, ?> idMap) {
-               this.entityName = entityName;
-
-               if (idMap == null || idMap.size() == 0) {
-                       this.objectIdKeys = Collections.emptyMap();
-                       return;
-               }
-               this.objectIdKeys = wrapIdMap(idMap);
-       }
-
-       @SuppressWarnings("unchecked")
-       private Map<String, Object> wrapIdMap(Map<String, ?> m) {
-               if(m.getClass() == HashMap.class) {
-                       return (Map<String, Object>)m;
-               } else {
-                       // we have to create a copy of the map, otherwise we 
may run into serialization problems with hessian
-                       return new HashMap<>(m);
-               }
-       }
-
-       /**
-        * Is this is temporary object id (used for objects which are not yet
-        * persisted to the data store).
-        */
-       @Override
-       public boolean isTemporary() {
-               return false;
-       }
-
-       /**
-        * @since 1.2
-        */
-       @Override
-       public String getEntityName() {
-               return entityName;
-       }
-
-       /**
-        * Get the binary temporary object id. Null if this object id is 
permanent
-        * (persisted to the data store).
-        */
-       @Override
-       public byte[] getKey() {
-               return null;
-       }
-
-       /**
-        * Returns an unmodifiable Map of persistent id values, essentially a
-        * primary key map. For temporary id returns replacement id, if it was
-        * already created. Otherwise returns an empty map.
-        */
-       @Override
-       public Map<String, Object> getIdSnapshot() {
-               return Collections.unmodifiableMap(objectIdKeys);
-       }
-
-       @Override
-       public boolean equals(Object object) {
-               if (this == object) {
-                       return true;
-               }
-
-               if (!(object instanceof ObjectIdCompound)) {
-                       return false;
-               }
-
-               ObjectIdCompound id = (ObjectIdCompound) object;
-               if (!Objects.equals(entityName, id.entityName)) {
-                       return false;
-               }
-
-               if (id.objectIdKeys.size() != objectIdKeys.size()) {
-                       return false;
-               }
-
-               for (Map.Entry<String, ?> entry : objectIdKeys.entrySet()) {
-                       String entryKey = entry.getKey();
-                       Object entryValue = entry.getValue();
-
-                       if (entryValue == null
-                                       && (id.objectIdKeys.get(entryKey) != 
null || !id.objectIdKeys.containsKey(entryKey))) {
-                               return false;
-                       } else if (!valueEquals(entryValue, 
id.objectIdKeys.get(entryKey))) {
-                               return false;
-                       }
-               }
-
-               return true;
-       }
-
-       private boolean valueEquals(Object o1, Object o2) {
-               if (o1 == o2) {
-                       return true;
-               }
-
-               if (o2 == null) {
-                       return false;
-               }
-
-               if (o1 instanceof Number n1) {
-                       return o2 instanceof Number n2 && n1.longValue() == 
n2.longValue();
-               }
-
-               return Objects.deepEquals(o1, o2);
-       }
-
-       @Override
-       public int hashCode() {
-               if(hashCode != 0) {
-                       return hashCode;
-               }
-
-               HashCodeBuilder builder = new 
HashCodeBuilder().append(entityName.hashCode());
-
-               // handle multiple keys - must sort the keys to use with 
HashCodeBuilder
-               String[] keys = objectIdKeys.keySet().toArray(new String[0]);
-               Arrays.sort(keys);
-               for (int i = 0; i < keys.length; i++) {
-                       // HashCodeBuilder will take care of processing object 
if it
-                       // happens to be a primitive array such as byte[]
-
-                       // also we don't have to append the key hashcode, its 
index will work
-                       builder.append(i);
-
-                       Object value = objectIdKeys.get(keys[i]);
-                       // must reconcile all possible numeric types
-                       if (value instanceof Number number) {
-                               builder.append(number.longValue());
-                       } else {
-                               builder.append(value);
-                       }
-               }
-               return hashCode = builder.toHashCode();
-       }
-
-       /**
-        * Returns a non-null mutable map that can be used to append 
replacement id
-        * values. This allows to incrementally build a replacement GlobalID.
-        * 
-        * @since 1.2
-        */
-       @Override
-       public Map<String, Object> getReplacementIdMap() {
-               if (replacementIdMap == null) {
-                       replacementIdMap = new HashMap<>();
-               }
-
-               return replacementIdMap;
-       }
-
-       /**
-        * Creates and returns a replacement ObjectId. No validation of ID is 
done.
-        * 
-        * @since 1.2
-        */
-       @Override
-       public ObjectId createReplacementId() {
-               if(replacementIdMap == null) {
-                       return this;
-               }
-               // merge existing and replaced ids to handle a replaced subset 
of a compound primary key
-               Map<String, Object> newIdMap = new HashMap<>(objectIdKeys);
-               newIdMap.putAll(replacementIdMap);
-               return ObjectId.of(entityName, newIdMap);
-       }
-
-       /**
-        * Returns true if there is full or partial replacement id attached to 
this
-        * id. This method is preferable to "!getReplacementIdMap().isEmpty()" 
as
-        * it avoids unneeded replacement id map creation.
-        */
-       @Override
-       public boolean isReplacementIdAttached() {
-               return replacementIdMap != null && !replacementIdMap.isEmpty();
-       }
-
-       /**
-        * A standard toString method used for debugging. It is guaranteed to
-        * produce the same string if two ObjectIds are equal.
-        */
-       @Override
-       public String toString() {
-               StringBuilder buffer = new 
StringBuilder().append("<ObjectId:").append(entityName);
-               // ensure consistent order of the keys, so that toString could 
be
-               // used as a unique key, just like id itself
-               String[] keys = objectIdKeys.keySet().toArray(new String[0]);
-               Arrays.sort(keys);
-               for (String key : keys) {
-                       buffer.append(", 
").append(key).append("=").append(objectIdKeys.get(key));
-               }
-               return buffer.append(">").toString();
-       }
+    private static final long serialVersionUID = -2265029098344119323L;
+
+    protected final String entityName;
+    protected final Map<String, Object> objectIdKeys;
+
+    protected Map<String, Object> replacementIdMap;
+
+    // hash code is transient to make sure id is portable across VM
+    private transient int hashCode;
+
+    // exists for deserialization with Hessian and similar
+    @SuppressWarnings("unused")
+    private ObjectIdCompound() {
+        entityName = null;
+        objectIdKeys = Collections.emptyMap();
+    }
+
+    /**
+     * Creates a portable permanent ObjectId as a compound primary key.
+     *
+     * @param entityName The entity name which this object id is for
+     * @param idMap      Keys are usually the attribute names for each part of 
the
+     *                   primary key. Values are unique when taken as a whole.
+     * @since 1.2
+     */
+    ObjectIdCompound(String entityName, Map<String, ?> idMap) {
+        this.entityName = entityName;
+
+        if (idMap == null || idMap.size() == 0) {
+            this.objectIdKeys = Collections.emptyMap();
+            return;
+        }
+        this.objectIdKeys = wrapIdMap(idMap);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> wrapIdMap(Map<String, ?> m) {
+        if (m.getClass() == HashMap.class) {
+            return (Map<String, Object>) m;
+        } else {
+            // we have to create a copy of the map, otherwise we may run into 
serialization problems with hessian
+            return new HashMap<>(m);
+        }
+    }
+
+    /**
+     * Is this is temporary object id (used for objects which are not yet
+     * persisted to the data store).
+     */
+    @Override
+    public boolean isTemporary() {
+        return false;
+    }
+
+    /**
+     * @since 1.2
+     */
+    @Override
+    public String getEntityName() {
+        return entityName;
+    }
+
+    /**
+     * Get the binary temporary object id. Null if this object id is permanent
+     * (persisted to the data store).
+     */
+    @Override
+    public byte[] getKey() {
+        return null;
+    }
+
+    /**
+     * Returns an unmodifiable Map of persistent id values, essentially a
+     * primary key map. For temporary id returns replacement id, if it was
+     * already created. Otherwise returns an empty map.
+     */
+    @Override
+    public Map<String, Object> getIdSnapshot() {
+        return Collections.unmodifiableMap(objectIdKeys);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+
+        if (!(object instanceof ObjectIdCompound)) {
+            return false;
+        }
+
+        ObjectIdCompound id = (ObjectIdCompound) object;
+        if (!Objects.equals(entityName, id.entityName)) {
+            return false;
+        }
+
+        if (id.objectIdKeys.size() != objectIdKeys.size()) {
+            return false;
+        }
+
+        for (Map.Entry<String, ?> entry : objectIdKeys.entrySet()) {
+            String entryKey = entry.getKey();
+            Object entryValue = entry.getValue();
+
+            if (entryValue == null
+                    && (id.objectIdKeys.get(entryKey) != null || 
!id.objectIdKeys.containsKey(entryKey))) {
+                return false;
+            } else if (!valueEquals(entryValue, 
id.objectIdKeys.get(entryKey))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private boolean valueEquals(Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        }
+
+        if (o2 == null) {
+            return false;
+        }
+
+        if (o1 instanceof Number n1) {
+            return o2 instanceof Number n2 && n1.longValue() == n2.longValue();
+        }
+
+        return Objects.deepEquals(o1, o2);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode != 0) {
+            return hashCode;
+        }
+
+        // sort keys for order-independence
+        String[] keys = objectIdKeys.keySet().toArray(new String[0]);
+        Arrays.sort(keys);
+        int result = entityName.hashCode();
+        for (int i = 0; i < keys.length; i++) {
+            result = 31 * result + i;
+            Object value = objectIdKeys.get(keys[i]);
+            // reconcile all numeric types via long; use content hash for 
arrays
+            result = 31 * result + (value instanceof Number n ? 
Long.hashCode(n.longValue()) : ObjectIdSingle.deepHashCode(value));
+        }
+        return hashCode = result;
+    }
+
+    /**
+     * Returns a non-null mutable map that can be used to append replacement id
+     * values. This allows to incrementally build a replacement GlobalID.
+     *
+     * @since 1.2
+     */
+    @Override
+    public Map<String, Object> getReplacementIdMap() {
+        if (replacementIdMap == null) {
+            replacementIdMap = new HashMap<>();
+        }
+
+        return replacementIdMap;
+    }
+
+    /**
+     * Creates and returns a replacement ObjectId. No validation of ID is done.
+     *
+     * @since 1.2
+     */
+    @Override
+    public ObjectId createReplacementId() {
+        if (replacementIdMap == null) {
+            return this;
+        }
+        // merge existing and replaced ids to handle a replaced subset of a 
compound primary key
+        Map<String, Object> newIdMap = new HashMap<>(objectIdKeys);
+        newIdMap.putAll(replacementIdMap);
+        return ObjectId.of(entityName, newIdMap);
+    }
+
+    /**
+     * Returns true if there is full or partial replacement id attached to this
+     * id. This method is preferable to "!getReplacementIdMap().isEmpty()" as
+     * it avoids unneeded replacement id map creation.
+     */
+    @Override
+    public boolean isReplacementIdAttached() {
+        return replacementIdMap != null && !replacementIdMap.isEmpty();
+    }
+
+    /**
+     * A standard toString method used for debugging. It is guaranteed to
+     * produce the same string if two ObjectIds are equal.
+     */
+    @Override
+    public String toString() {
+        StringBuilder buffer = new 
StringBuilder().append("<ObjectId:").append(entityName);
+        // ensure consistent order of the keys, so that toString could be
+        // used as a unique key, just like id itself
+        String[] keys = objectIdKeys.keySet().toArray(new String[0]);
+        Arrays.sort(keys);
+        for (String key : keys) {
+            buffer.append(", 
").append(key).append("=").append(objectIdKeys.get(key));
+        }
+        return buffer.append(">").toString();
+    }
 }
diff --git a/cayenne/src/main/java/org/apache/cayenne/ObjectIdSingle.java 
b/cayenne/src/main/java/org/apache/cayenne/ObjectIdSingle.java
index ee0df252e..c868db55a 100644
--- a/cayenne/src/main/java/org/apache/cayenne/ObjectIdSingle.java
+++ b/cayenne/src/main/java/org/apache/cayenne/ObjectIdSingle.java
@@ -19,15 +19,16 @@
 
 package org.apache.cayenne;
 
+import org.apache.cayenne.util.SingleEntryMap;
+
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
-
-import org.apache.cayenne.util.HashCodeBuilder;
-import org.apache.cayenne.util.SingleEntryMap;
 import java.util.Objects;
 
 /**
  * {@link ObjectId} with single non-numeric value
+ *
  * @since 4.2
  */
 class ObjectIdSingle implements ObjectId {
@@ -77,7 +78,7 @@ class ObjectIdSingle implements ObjectId {
 
     @Override
     public Map<String, Object> getReplacementIdMap() {
-        if(replacementId == null) {
+        if (replacementId == null) {
             replacementId = new SingleEntryMap<>(keyName);
         }
         return replacementId;
@@ -116,7 +117,7 @@ class ObjectIdSingle implements ObjectId {
             return false;
         }
         ObjectIdSingle that = (ObjectIdSingle) o;
-        if(!Objects.equals(entityName, that.entityName)) {
+        if (!Objects.equals(entityName, that.entityName)) {
             return false;
         }
         return Objects.deepEquals(value, that.value);
@@ -124,9 +125,22 @@ class ObjectIdSingle implements ObjectId {
 
     @Override
     public int hashCode() {
-        if(hashCode == 0) {
-            hashCode = new 
HashCodeBuilder().append(entityName).append(value).toHashCode();
+        if (hashCode == 0) {
+            hashCode = 31 * Objects.hashCode(entityName) + deepHashCode(value);
         }
         return hashCode;
     }
+
+    static int deepHashCode(Object v) {
+        if (v instanceof byte[] a) return Arrays.hashCode(a);
+        if (v instanceof int[] a) return Arrays.hashCode(a);
+        if (v instanceof long[] a) return Arrays.hashCode(a);
+        if (v instanceof short[] a) return Arrays.hashCode(a);
+        if (v instanceof char[] a) return Arrays.hashCode(a);
+        if (v instanceof float[] a) return Arrays.hashCode(a);
+        if (v instanceof double[] a) return Arrays.hashCode(a);
+        if (v instanceof boolean[] a) return Arrays.hashCode(a);
+        if (v instanceof Object[] a) return Arrays.deepHashCode(a);
+        return Objects.hashCode(v);
+    }
 }
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java 
b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
index 73a415583..74d7c09d9 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
@@ -26,7 +26,6 @@ import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ProcedureParameter;
-import org.apache.cayenne.util.HashCodeBuilder;
 
 import java.util.Objects;
 import org.apache.cayenne.util.ToStringBuilder;
@@ -192,8 +191,7 @@ public class ColumnDescriptor {
      */
     @Override
     public int hashCode() {
-        return new HashCodeBuilder(23, 
43).append(name).append(namePrefix).append(procedureName).append(tableName)
-                .append(dataRowKey).toHashCode();
+        return Objects.hash(name, namePrefix, procedureName, tableName, 
dataRowKey);
     }
 
     /**
diff --git a/cayenne/src/main/java/org/apache/cayenne/event/EventSubject.java 
b/cayenne/src/main/java/org/apache/cayenne/event/EventSubject.java
index 7cc0fdc16..27ea695cd 100644
--- a/cayenne/src/main/java/org/apache/cayenne/event/EventSubject.java
+++ b/cayenne/src/main/java/org/apache/cayenne/event/EventSubject.java
@@ -22,7 +22,7 @@ package org.apache.cayenne.event;
 import java.io.Serializable;
 import java.util.Map;
 
-import org.apache.cayenne.util.HashCodeBuilder;
+import java.util.Objects;
 import org.apache.cayenne.util.WeakValueMap;
 
 /**
@@ -103,9 +103,7 @@ public class EventSubject implements Serializable {
 
     @Override
     public int hashCode() {
-        return new HashCodeBuilder(17, 3)
-                .append(_fullyQualifiedSubjectName)
-                .toHashCode();
+        return Objects.hashCode(_fullyQualifiedSubjectName);
     }
 
     public String getSubjectName() {
diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/Expression.java 
b/cayenne/src/main/java/org/apache/cayenne/exp/Expression.java
index 6f25938b0..46f0f1e38 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/Expression.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/Expression.java
@@ -32,7 +32,6 @@ import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.exp.parser.ASTScalar;
 import org.apache.cayenne.util.ConversionUtil;
-import org.apache.cayenne.util.HashCodeBuilder;
 import org.apache.cayenne.util.XMLEncoder;
 
 import java.util.Objects;
@@ -296,12 +295,12 @@ public abstract class Expression implements Serializable, 
XMLSerializable {
 
        @Override
        public int hashCode() {
-               HashCodeBuilder builder = new 
HashCodeBuilder().append(getType());
+               int result = getType();
                int opCount = getOperandCount();
-               for(int i=0; i<opCount; i++) {
-                       builder.append(getOperand(i));
+               for (int i = 0; i < opCount; i++) {
+                       result = 31 * result + Objects.hashCode(getOperand(i));
                }
-               return builder.toHashCode();
+               return result;
        }
 
        /**
diff --git a/cayenne/src/main/java/org/apache/cayenne/util/HashCodeBuilder.java 
b/cayenne/src/main/java/org/apache/cayenne/util/HashCodeBuilder.java
index e189a363c..dd1e9e512 100644
--- a/cayenne/src/main/java/org/apache/cayenne/util/HashCodeBuilder.java
+++ b/cayenne/src/main/java/org/apache/cayenne/util/HashCodeBuilder.java
@@ -21,9 +21,11 @@ package org.apache.cayenne.util;
 /**
  * Assists in implementing {@link Object#hashCode()} methods. The code is 
based on
  * HashCodeBuilder from commons-lang 2.1.
- * 
+ *
  * @since 3.0
+ * @deprecated use {@link java.util.Objects#hash(Object...)} or a manual 
{@code 31 * h + Objects.hashCode(x)} loop
  */
+@Deprecated(since = "5.0", forRemoval = true)
 public class HashCodeBuilder {
 
     /**
diff --git a/cayenne/src/main/java/org/apache/cayenne/util/Util.java 
b/cayenne/src/main/java/org/apache/cayenne/util/Util.java
index 02d9bfd4e..7ff057a54 100644
--- a/cayenne/src/main/java/org/apache/cayenne/util/Util.java
+++ b/cayenne/src/main/java/org/apache/cayenne/util/Util.java
@@ -590,11 +590,11 @@ public class Util {
      */
     @Deprecated(since = "5.0", forRemoval = true)
     public static int hashCode(Collection<?> c) {
-        HashCodeBuilder builder = new HashCodeBuilder();
+        int result = 17;
         for (Object o : c) {
-            builder.append(o);
+            result = 37 * result + Objects.hashCode(o);
         }
-        return builder.toHashCode();
+        return result;
     }
 
     /**

Reply via email to