From abe66ce0d0e0cbd917c6a08606e7fb4f5f46bc3c Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Thu, 25 Feb 2021 17:56:51 +0200 Subject: [PATCH] False positive "class should override both the equals and hashCode methods" for Enum values #295 --- .../hibernate/type/util/ReflectionUtils.java | 17 ++++ .../json/internal/JsonTypeDescriptor.java | 9 +- .../hibernate/type/util/ReflectionUtils.java | 17 ++++ .../json/internal/JsonTypeDescriptor.java | 9 +- .../hibernate/type/util/ReflectionUtils.java | 17 ++++ .../json/internal/JsonTypeDescriptor.java | 9 +- .../hibernate/type/util/ReflectionUtils.java | 17 ++++ .../type/json/PostgreSQLJsonListEnumTest.java | 95 +++++++++++++++++++ 8 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/PostgreSQLJsonListEnumTest.java diff --git a/hibernate-types-4/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java b/hibernate-types-4/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java index 6246cb2d3..0bfdd84f7 100644 --- a/hibernate-types-4/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java +++ b/hibernate-types-4/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java @@ -195,6 +195,23 @@ public static Method getMethod(Object target, String methodName, Class... parame return getMethod(target.getClass(), methodName, parameterTypes); } + /** + * Get the {@link Method} with the given signature (name and parameter types) belonging to + * the provided Java {@link Object} or {@code null} if no {@link Method} was found. + * + * @param targetClass target {@link Class} + * @param methodName method name + * @param parameterTypes method parameter types + * @return return {@link Method} matching the provided signature or {@code null} + */ + public static Method getMethodOrNull(Class targetClass, String methodName, Class... parameterTypes) { + try { + return getMethod(targetClass, methodName, parameterTypes); + } catch (RuntimeException e) { + return null; + } + } + /** * Get the {@link Method} with the given signature (name and parameter types) belonging to * the provided Java {@link Object} or {@code null} if no {@link Method} was found. diff --git a/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java b/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java index f46d45b58..84e906af6 100644 --- a/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java +++ b/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java @@ -187,10 +187,13 @@ private void validatePropertyType() { continue; } validatedTypes.add(genericType); - Method equalsMethod = ReflectionUtils.getDeclaredMethodOrNull(genericType, "equals", Object.class); - Method hashCodeMethod = ReflectionUtils.getDeclaredMethodOrNull(genericType, "hashCode"); + Method equalsMethod = ReflectionUtils.getMethodOrNull(genericType, "equals", Object.class); + Method hashCodeMethod = ReflectionUtils.getMethodOrNull(genericType, "hashCode"); - if(equalsMethod == null || hashCodeMethod == null) { + if(equalsMethod == null || + hashCodeMethod == null || + Object.class.equals(equalsMethod.getDeclaringClass()) || + Object.class.equals(hashCodeMethod.getDeclaringClass())) { LogUtils.LOGGER.warn("The {} class should override both the equals and hashCode methods based on the JSON object value it represents!", genericType); } } diff --git a/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java b/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java index 6246cb2d3..0bfdd84f7 100644 --- a/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java +++ b/hibernate-types-43/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java @@ -195,6 +195,23 @@ public static Method getMethod(Object target, String methodName, Class... parame return getMethod(target.getClass(), methodName, parameterTypes); } + /** + * Get the {@link Method} with the given signature (name and parameter types) belonging to + * the provided Java {@link Object} or {@code null} if no {@link Method} was found. + * + * @param targetClass target {@link Class} + * @param methodName method name + * @param parameterTypes method parameter types + * @return return {@link Method} matching the provided signature or {@code null} + */ + public static Method getMethodOrNull(Class targetClass, String methodName, Class... parameterTypes) { + try { + return getMethod(targetClass, methodName, parameterTypes); + } catch (RuntimeException e) { + return null; + } + } + /** * Get the {@link Method} with the given signature (name and parameter types) belonging to * the provided Java {@link Object} or {@code null} if no {@link Method} was found. diff --git a/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java b/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java index f7b400376..046038172 100644 --- a/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java +++ b/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java @@ -187,10 +187,13 @@ private void validatePropertyType() { continue; } validatedTypes.add(genericType); - Method equalsMethod = ReflectionUtils.getDeclaredMethodOrNull(genericType, "equals", Object.class); - Method hashCodeMethod = ReflectionUtils.getDeclaredMethodOrNull(genericType, "hashCode"); + Method equalsMethod = ReflectionUtils.getMethodOrNull(genericType, "equals", Object.class); + Method hashCodeMethod = ReflectionUtils.getMethodOrNull(genericType, "hashCode"); - if(equalsMethod == null || hashCodeMethod == null) { + if(equalsMethod == null || + hashCodeMethod == null || + Object.class.equals(equalsMethod.getDeclaringClass()) || + Object.class.equals(hashCodeMethod.getDeclaringClass())) { LogUtils.LOGGER.warn("The {} class should override both the equals and hashCode methods based on the JSON object value it represents!", genericType); } } diff --git a/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java b/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java index 6246cb2d3..0bfdd84f7 100644 --- a/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java +++ b/hibernate-types-5/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java @@ -195,6 +195,23 @@ public static Method getMethod(Object target, String methodName, Class... parame return getMethod(target.getClass(), methodName, parameterTypes); } + /** + * Get the {@link Method} with the given signature (name and parameter types) belonging to + * the provided Java {@link Object} or {@code null} if no {@link Method} was found. + * + * @param targetClass target {@link Class} + * @param methodName method name + * @param parameterTypes method parameter types + * @return return {@link Method} matching the provided signature or {@code null} + */ + public static Method getMethodOrNull(Class targetClass, String methodName, Class... parameterTypes) { + try { + return getMethod(targetClass, methodName, parameterTypes); + } catch (RuntimeException e) { + return null; + } + } + /** * Get the {@link Method} with the given signature (name and parameter types) belonging to * the provided Java {@link Object} or {@code null} if no {@link Method} was found. diff --git a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java index 2e0b0f595..5cdb1dc56 100644 --- a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java +++ b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/json/internal/JsonTypeDescriptor.java @@ -186,10 +186,13 @@ private void validatePropertyType() { continue; } validatedTypes.add(genericType); - Method equalsMethod = ReflectionUtils.getDeclaredMethodOrNull(genericType, "equals", Object.class); - Method hashCodeMethod = ReflectionUtils.getDeclaredMethodOrNull(genericType, "hashCode"); + Method equalsMethod = ReflectionUtils.getMethodOrNull(genericType, "equals", Object.class); + Method hashCodeMethod = ReflectionUtils.getMethodOrNull(genericType, "hashCode"); - if(equalsMethod == null || hashCodeMethod == null) { + if(equalsMethod == null || + hashCodeMethod == null || + Object.class.equals(equalsMethod.getDeclaringClass()) || + Object.class.equals(hashCodeMethod.getDeclaringClass())) { LogUtils.LOGGER.warn("The {} class should override both the equals and hashCode methods based on the JSON object value it represents!", genericType); } } diff --git a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java index 6586f1a23..daace48fa 100644 --- a/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java +++ b/hibernate-types-52/src/main/java/com/vladmihalcea/hibernate/type/util/ReflectionUtils.java @@ -239,6 +239,23 @@ public static Method getMethod(Class targetClass, String methodName, Class... pa } } + /** + * Get the {@link Method} with the given signature (name and parameter types) belonging to + * the provided Java {@link Object} or {@code null} if no {@link Method} was found. + * + * @param targetClass target {@link Class} + * @param methodName method name + * @param parameterTypes method parameter types + * @return return {@link Method} matching the provided signature or {@code null} + */ + public static Method getMethodOrNull(Class targetClass, String methodName, Class... parameterTypes) { + try { + return getMethod(targetClass, methodName, parameterTypes); + } catch (RuntimeException e) { + return null; + } + } + /** * Get the {@link Method} with the given signature (name and parameter types) belonging to * the provided Java {@link Class}, excluding inherited ones, or {@code null} if no {@link Method} was found. diff --git a/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/PostgreSQLJsonListEnumTest.java b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/PostgreSQLJsonListEnumTest.java new file mode 100644 index 000000000..2dc27a158 --- /dev/null +++ b/hibernate-types-52/src/test/java/com/vladmihalcea/hibernate/type/json/PostgreSQLJsonListEnumTest.java @@ -0,0 +1,95 @@ +package com.vladmihalcea.hibernate.type.json; + +import com.vladmihalcea.hibernate.type.util.AbstractPostgreSQLIntegrationTest; +import org.hibernate.Session; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.junit.Test; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +public class PostgreSQLJsonListEnumTest extends AbstractPostgreSQLIntegrationTest { + + @Override + protected Class[] entities() { + return new Class[]{ + Book.class + }; + } + + @Test + public void test() { + + doInJPA(entityManager -> { + entityManager.persist( + new Book() + .setIsbn("978-9730228236") + .addProperty(PropertyType.BEST_SELLER) + .addProperty(PropertyType.FREE_CHAPTER) + ); + }); + + doInJPA(entityManager -> { + Book book = entityManager.unwrap(Session.class) + .bySimpleNaturalId(Book.class) + .load("978-9730228236"); + + List bookProperties = book.getProperties(); + assertEquals(2, bookProperties.size()); + }); + } + + @Entity(name = "Book") + @Table(name = "book") + @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) + public static class Book { + + @Id + @GeneratedValue + private Long id; + + @NaturalId + @Column(length = 15) + private String isbn; + + @Type(type = "jsonb") + @Column(columnDefinition = "jsonb") + private List propertyTypes = new ArrayList<>(); + + public String getIsbn() { + return isbn; + } + + public Book setIsbn(String isbn) { + this.isbn = isbn; + return this; + } + + public List getProperties() { + return propertyTypes; + } + + public Book setProperties(List properties) { + this.propertyTypes = properties; + return this; + } + + public Book addProperty(PropertyType propertyType) { + propertyTypes.add(propertyType); + return this; + } + } + + public enum PropertyType { + BEST_SELLER, + FREE_CHAPTER + } +} \ No newline at end of file