Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

surrogate key suppression #698

Closed
xenoterracide opened this issue Sep 14, 2022 · 10 comments
Closed

surrogate key suppression #698

xenoterracide opened this issue Sep 14, 2022 · 10 comments
Labels

Comments

@xenoterracide
Copy link

xenoterracide commented Sep 14, 2022

Describe the bug

equalsverifier tells me to suppress surrogate key and then it stacks when I do so

To Reproduce

package com.capitalone.e1.domain.core

import nl.jqno.equalsverifier.EqualsVerifier
import nl.jqno.equalsverifier.Warning
import org.junit.jupiter.api.Test

internal open class ExceptionEntityTest {

  @Test
  fun equality() {
    EqualsVerifier.forClass(ExceptionEntity::class.java)
      .withOnlyTheseFields("id")
      .suppress(Warning.SURROGATE_KEY)
      .verify()
  }
}

Code that triggers the behavior

package com.capitalone.e1.util.jpa

import com.github.f4b6a3.uuid.UuidCreator
import java.io.Serializable
import java.util.Objects
import java.util.UUID
import javax.persistence.MappedSuperclass

@MappedSuperclass
abstract class AbstractId : Serializable {

  protected var id: UUID = UuidCreator.getTimeOrdered()

  /**
   * checks that other is the same instance of this
   */
  protected abstract fun canEqual(other: AbstractId): Boolean

  final override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other is AbstractId && other.canEqual(this) && this.id == other.id) return true
    return false
  }

  final override fun hashCode(): Int = Objects.hash(this.id)

  companion object {
    @Transient
    const val serialVersionUID = 1L
  }
}
package com.capitalone.e1.domain.core

import com.capitalone.e1.util.jpa.AbstractId
import javax.persistence.Embeddable

@Embeddable
class ExceptionEntityId : AbstractId() {
  override fun canEqual(other: AbstractId): Boolean = other is ExceptionEntityId
}
package com.capitalone.e1.util.jpa

import org.apache.commons.lang3.builder.ToStringBuilder
import org.apache.commons.lang3.builder.ToStringStyle
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import java.time.OffsetDateTime
import java.util.Objects
import javax.persistence.Access
import javax.persistence.AccessType
import javax.persistence.Column
import javax.persistence.EmbeddedId
import javax.persistence.MappedSuperclass
import javax.persistence.Version

@Suppress("UnnecessaryAbstractClass") // this should not and could not b e directly instantiated
@MappedSuperclass
abstract class AbstractBaseEntity<ID : AbstractId> {

  @get:EmbeddedId
  @get:Access(AccessType.PROPERTY)
  @get:Column(columnDefinition = "uuid", nullable = false, updatable = false, unique = true)
  abstract var id: ID
    protected set

  @Version
  protected open var version: Int = 0

  @CreatedDate
  @Column(name = "created_on", columnDefinition = "timestamp with time zone", nullable = false, updatable = false)
  open var createdOn: OffsetDateTime? = null
    protected set

  @LastModifiedDate
  @Column(name = "last_modified_on", columnDefinition = "timestamp with time zone", nullable = false)
  open var lastModifiedOn: OffsetDateTime? = null
    protected set

  /**
   * checks that other is the same instance of this
   */
  abstract fun canEqual(other: AbstractBaseEntity<*>): Boolean

  final override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other is AbstractBaseEntity<*> && other.canEqual(this) && this.id == other.id) return true
    return false
  }

  final override fun hashCode(): Int = Objects.hash(this.id)

  override fun toString(): String {
    return ToStringBuilder.reflectionToString(
      this,
      ToStringStyle.MULTI_LINE_STYLE
    )
  }
}
package com.capitalone.e1.domain.core

import com.capitalone.e1.util.jpa.AbstractBaseEntity
import javax.persistence.Column
import javax.persistence.EmbeddedId
import javax.persistence.Entity
import javax.persistence.Table

@Entity
@Table(name = "exceptions")
open class ExceptionEntity() : AbstractBaseEntity<ExceptionEntityId>() {

  @Column(name = "business_division_id", columnDefinition = "text")
  open var businessDivisionId: String? = null
    protected set

  @EmbeddedId
  override var id: ExceptionEntityId = ExceptionEntityId()

  constructor(businessDivisionId: String) : this() {
    this.businessDivisionId = businessDivisionId
  }

  override fun canEqual(other: AbstractBaseEntity<*>): Boolean = other is ExceptionEntity
}

Error message

Precondition: you can't use withOnlyTheseFields when Warning.SURROGATE_KEY is suppressed.
java.lang.IllegalStateException: Precondition: you can't use withOnlyTheseFields when Warning.SURROGATE_KEY is suppressed.
	at nl.jqno.equalsverifier.internal.util.Validations.validate(Validations.java:232)
	at nl.jqno.equalsverifier.internal.util.Validations.validateWarningsAndFields(Validations.java:116)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.suppress(SingleTypeEqualsVerifierApi.java:89)
	at com.capitalone.e1.domain.core.ExceptionEntityTest.equality(ExceptionEntityTest.kt:13)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

if I remove the supress I get

qualsVerifier found a problem in class com.capitalone.e1.domain.core.ExceptionEntity.
-> Precondition: you can't use withOnlyTheseFields on a field marked @Id or @EmbeddedId.
Suppress Warning.SURROGATE_KEY if you want to use only the @Id or @EmbeddedId fields in equals.

For more information, go to: https://www.jqno.nl/equalsverifier/errormessages
java.lang.AssertionError: EqualsVerifier found a problem in class com.capitalone.e1.domain.core.ExceptionEntity.
-> Precondition: you can't use withOnlyTheseFields on a field marked @Id or @EmbeddedId.
Suppress Warning.SURROGATE_KEY if you want to use only the @Id or @EmbeddedId fields in equals.

For more information, go to: https://www.jqno.nl/equalsverifier/errormessages
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:316)
	at com.capitalone.e1.domain.core.ExceptionEntityTest.equality(ExceptionEntityTest.kt:12)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.lang.IllegalStateException: Precondition: you can't use withOnlyTheseFields on a field marked @Id or @EmbeddedId.
Suppress Warning.SURROGATE_KEY if you want to use only the @Id or @EmbeddedId fields in equals.
	at nl.jqno.equalsverifier.internal.util.Validations.validate(Validations.java:232)
	at nl.jqno.equalsverifier.internal.util.Validations.validateFieldAnnotation(Validations.java:198)
	at nl.jqno.equalsverifier.internal.util.Validations.lambda$validateFieldAnnotations$3(Validations.java:189)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at nl.jqno.equalsverifier.internal.util.Validations.validateFieldAnnotations(Validations.java:189)
	at nl.jqno.equalsverifier.internal.util.Validations.validateProcessedAnnotations(Validations.java:145)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.performVerification(SingleTypeEqualsVerifierApi.java:368)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:312)
	... 86 more

Expected behavior

prior to using @EmbeddedId instead using @Id there was no error or suppression required. I would expect the behavior to remain the same.

Version
What version of EqualsVerifier are you using?

nl.jqno.equalsverifier:equalsverifier:3.10.1

Additional context

I'm not sure if embeddedid should be considered surrogate on the basis that it's for compound keys. Mine certainly is, but as a generalization. Also, should an @Version be considered part of the equality check for the entitity?

@jqno
Copy link
Owner

jqno commented Sep 16, 2022

The idea of Warning.SURROGATE_KEY is that it will pick up the @Id and @EmbeddedId fields automatically, so you don't have to specify them manually.

@Version fields are not included in this, mostly because I was not aware of @Version at the time when I wrote that feature -- I've never been a big Hibernate user. Maybe it should indeed be considered, as you suggest, I don't know...what do you think?

Anyway, I'll leave this issue open for now as a reminder for me to add some documentation about this, because I agree that it's not obvious.

@xenoterracide
Copy link
Author

@Version fields are not included in this, mostly because I was not aware of @Version at the time when I wrote that feature -- I've never been a big Hibernate user. Maybe it should indeed be considered, as you suggest, I don't know...what do you think?

well, obviously it's a feature of JPA not hibernate ;). I'm not sure @Version should be or not. If I had 2 different version numbers but the same Id that would mean the same entity but with different data. The question is, for set purposes are they equal, I'm not sure. Maybe they shouldn't be.

Either way, I'm not able to find a way to make equalsverifier work with @EmbeddedId, suppressing or not I get an error. So my biggest focus is, how do I get equalsverifier passing again.

@jqno
Copy link
Owner

jqno commented Sep 16, 2022

You should keep the suppress, and remove the withOnlyTheseFields call.

If that doesn't work, can you please make a small zip file with a complete project that I can import directly into my IDE, preferably using Java instead of Kotlin? It will increase the chances of me being able to take a further look at this within a reasonable amount of time.

@xenoterracide
Copy link
Author

yeah, that worked. I wonder if maybe it's worth just a little more documentation though if @Version field is what triggered the problem.

@jqno
Copy link
Owner

jqno commented Sep 16, 2022

Glad to hear it worked!

I think the @Version field doesn't have anything to do with it (unless you included it in your equals method, but I didn't see it in the code you posted). It's simply that if you suppress the SURROGATE_KEY warning, EqualsVerifier automatically searches for the @Id and @EmbeddedId fields. Anything you would do in withOnlyTheseFields would then be either redundant (if you pass it id) or contradictory (if you pass it anything else), so EqualsVerifier tells you to remove the entire call.

But I agree that it tells you so in a confusing way, so I'll definitely make the error message more clear, and add a paragraph in de documentation on the website. I'll leave the issue open until I do so.

@xenoterracide
Copy link
Author

It would be interesting to here your thoughts on whether an @Version field should be part of an "equals/hashcode". I mean obviously it's an optimistic lock and all this applies to more than hibernate (that's just a very common database mapping tool).

@xenoterracide
Copy link
Author

To be clear it's about whether an aggregate, which has a long lifecycle where it changes over time (imagine an object that changes in memory), is it still equal to itself even though the properties are different. E.g. if I clone and make a change, but I don't change the surrogate id.

@jqno
Copy link
Owner

jqno commented Sep 23, 2022

To be honest, I don't have enough experience with JPA in general to be able to make an informed call on this. I've simply never used @Version. What would you (or your colleagues) normally do?

@xenoterracide
Copy link
Author

Don't know, but it's not a jpa specific problem. Version is just a convenience method for an optimistic lock. Any database with multiple concurrent users might need this solution.

@jqno
Copy link
Owner

jqno commented Nov 30, 2022

Error messages have been improved in EqualsVerifier 3.12.

I've thought about the @Version thing some more, and decided to leave things as they are, at least for now, until I find a good argument on the subject.

@jqno jqno closed this as completed Nov 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants