Polishing.

Introduce EnversRevisionEntityInformation to reflect envers-specific revision information. Refactor DefaultRevisionEntityInformation to enum to keep a singleton around. Refine tests.

See #2850
Original pull request: #4003
This commit is contained in:
Mark Paluch
2025-09-16 16:37:39 +02:00
parent 8c3a0679bd
commit aa900658b3
8 changed files with 156 additions and 68 deletions
@@ -24,21 +24,27 @@ import org.springframework.data.repository.history.support.RevisionEntityInforma
* @author Oliver Gierke
* @author Chaedong Im
*/
class DefaultRevisionEntityInformation implements RevisionEntityInformation {
enum DefaultRevisionEntityInformation implements EnversRevisionEntityInformation {
INSTANCE;
@Override
public Class<?> getRevisionNumberType() {
return Integer.class;
}
@Override
public boolean isDefaultRevisionEntity() {
return true;
}
@Override
public Class<?> getRevisionEntityClass() {
return DefaultRevisionEntity.class;
}
public String getRevisionTimestampFieldName() {
@Override
public String getRevisionTimestampPropertyName() {
return "timestamp";
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2025 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
*
* https://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.springframework.data.envers.repository.support;
import org.springframework.data.repository.history.support.RevisionEntityInformation;
/**
* Envers-specific extension to {@link RevisionEntityInformation}.
*
* @author Mark Paluch
* @since 4.0
*/
public interface EnversRevisionEntityInformation extends RevisionEntityInformation {
/**
* Return the name of the timestamp property (annotated with {@link org.hibernate.envers.RevisionTimestamp}).
*
* @return the name of the timestamp property,
*/
String getRevisionTimestampPropertyName();
}
@@ -20,8 +20,8 @@ import jakarta.persistence.EntityManager;
import java.util.Optional;
import org.hibernate.envers.DefaultRevisionEntity;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
@@ -90,7 +90,7 @@ public class EnversRevisionRepositoryFactoryBean<T extends RevisionRepository<S,
this.revisionEntityInformation = Optional.ofNullable(revisionEntityClass) //
.filter(it -> !it.equals(DefaultRevisionEntity.class))//
.<RevisionEntityInformation> map(ReflectionRevisionEntityInformation::new) //
.orElseGet(DefaultRevisionEntityInformation::new);
.orElse(DefaultRevisionEntityInformation.INSTANCE);
}
@Override
@@ -218,12 +218,10 @@ public class EnversRevisionRepositoryImpl<T, ID, N extends Number & Comparable<N
}
private String getRevisionTimestampFieldName() {
if (revisionEntityInformation instanceof ReflectionRevisionEntityInformation reflection) {
return reflection.getRevisionTimestampFieldName();
} else if (revisionEntityInformation instanceof DefaultRevisionEntityInformation defaultInfo) {
return defaultInfo.getRevisionTimestampFieldName();
if (revisionEntityInformation instanceof EnversRevisionEntityInformation reflection) {
return reflection.getRevisionTimestampPropertyName();
} else {
return "timestamp";
return DefaultRevisionEntityInformation.INSTANCE.getRevisionTimestampPropertyName();
}
}
@@ -30,7 +30,7 @@ import org.springframework.util.ReflectionUtils;
* @author Oliver Gierke
* @author Chaedong Im
*/
public class ReflectionRevisionEntityInformation implements RevisionEntityInformation {
public class ReflectionRevisionEntityInformation implements EnversRevisionEntityInformation {
private final Class<?> revisionEntityClass;
private final Class<?> revisionNumberType;
@@ -54,22 +54,25 @@ public class ReflectionRevisionEntityInformation implements RevisionEntityInform
this.revisionNumberType = revisionNumberFieldCallback.getRequiredType();
this.revisionTimestampFieldName = revisionTimestampFieldCallback.getRequiredField().getName();
this.revisionEntityClass = revisionEntityClass;
}
@Override
public boolean isDefaultRevisionEntity() {
return false;
}
@Override
public Class<?> getRevisionEntityClass() {
return this.revisionEntityClass;
}
@Override
public Class<?> getRevisionNumberType() {
return this.revisionNumberType;
}
public String getRevisionTimestampFieldName() {
@Override
public String getRevisionTimestampPropertyName() {
return this.revisionTimestampFieldName;
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2025 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
*
* https://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.springframework.data.envers.repository.support;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link DefaultRevisionEntityInformation}.
*
* @author Mark Paluch
* @author Chaedong Im
*/
class DefaultRevisionEntityInformationUnitTests {
@Test // GH-2850
void defaultRevisionEntityInformationReturnsStandardTimestampFieldName() {
DefaultRevisionEntityInformation revisionInfo = DefaultRevisionEntityInformation.INSTANCE;
assertThat(revisionInfo.getRevisionTimestampPropertyName()).isEqualTo("timestamp");
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2025 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
*
* https://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.springframework.data.envers.repository.support;
import static org.assertj.core.api.Assertions.*;
import org.hibernate.envers.RevisionNumber;
import org.hibernate.envers.RevisionTimestamp;
import org.junit.jupiter.api.Test;
import org.springframework.data.envers.sample.CustomRevisionEntity;
/**
* Unit tests for {@link ReflectionRevisionEntityInformation}.
*
* @author Mark Paluch
* @author Chaedong Im
*/
class ReflectionRevisionEntityInformationUnitTests {
@Test // GH-2850
void reflectionRevisionEntityInformationDetectsStandardTimestampField() {
ReflectionRevisionEntityInformation revisionInfo = new ReflectionRevisionEntityInformation(
CustomRevisionEntity.class);
assertThat(revisionInfo.getRevisionTimestampPropertyName()).isEqualTo("timestamp");
}
@Test // GH-2850
void reflectionRevisionEntityInformationDetectsCustomTimestampField() {
ReflectionRevisionEntityInformation revisionInfo = new ReflectionRevisionEntityInformation(
WithCustomTimestampPropertyName.class);
assertThat(revisionInfo.getRevisionTimestampPropertyName()).isEqualTo("myCustomTimestamp");
}
/**
* Custom revision entity with a non-standard timestamp field name to test dynamic timestamp property detection.
*
* @author Chaedong Im
*/
private static class WithCustomTimestampPropertyName {
@RevisionNumber private int revisionId;
@RevisionTimestamp private long myCustomTimestamp; // Non-standard field name
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2025 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
*
* https://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.springframework.data.envers.sample;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.RevisionNumber;
import org.hibernate.envers.RevisionTimestamp;
/**
* Custom revision entity with a non-standard timestamp field name to test dynamic timestamp property detection.
*
* @author Chaedong Im
*/
@Entity
@RevisionEntity
public class CustomRevisionEntityWithDifferentTimestamp {
@Id @GeneratedValue @RevisionNumber
private int revisionId;
@RevisionTimestamp
private long myCustomTimestamp; // Non-standard field name
public int getRevisionId() {
return revisionId;
}
public void setRevisionId(int revisionId) {
this.revisionId = revisionId;
}
public long getMyCustomTimestamp() {
return myCustomTimestamp;
}
public void setMyCustomTimestamp(long myCustomTimestamp) {
this.myCustomTimestamp = myCustomTimestamp;
}
}