Initial draft of Envers support for Spring Data JPA.
Added an implementation of the Spring Data Commons historiography API based on Hibernate Envers. The implementation is a contribution of @hygl to a large degree.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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.hibernate.envers.DefaultRevisionEntity;
|
||||
import org.springframework.data.repository.history.support.RevisionEntityInformation;
|
||||
|
||||
/**
|
||||
* {@link RevisionEntityInformation} for {@link DefaultRevisionEntity}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class DefaultRevisionEntityInformation implements RevisionEntityInformation {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionNumberType()
|
||||
*/
|
||||
public Class<?> getRevisionNumberType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.support.RevisionEntityInformation#isDefaultRevisionEntity()
|
||||
*/
|
||||
public boolean isDefaultRevisionEntity() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionEntityClass()
|
||||
*/
|
||||
public Class<?> getRevisionEntityClass() {
|
||||
return DefaultRevisionEntity.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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.hibernate.envers.DefaultRevisionEntity;
|
||||
import org.joda.time.DateTime;
|
||||
import org.springframework.data.history.RevisionMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link RevisionMetadata} working with a {@link DefaultRevisionEntity}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Philip Huegelmeyer
|
||||
*/
|
||||
public class DefaultRevisionMetadata implements RevisionMetadata<Integer> {
|
||||
|
||||
private final DefaultRevisionEntity entity;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultRevisionMetadata}.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
*/
|
||||
public DefaultRevisionMetadata(DefaultRevisionEntity entity) {
|
||||
|
||||
Assert.notNull(entity);
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.history.RevisionMetadata#getRevisionNumber()
|
||||
*/
|
||||
public Integer getRevisionNumber() {
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.history.RevisionMetadata#getRevisionDate()
|
||||
*/
|
||||
public DateTime getRevisionDate() {
|
||||
return new DateTime(entity.getTimestamp());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.history.RevisionMetadata#getDelegate()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getDelegate() {
|
||||
return (T) entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 java.io.Serializable;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.history.RevisionRepository;
|
||||
|
||||
/**
|
||||
* Convenience interface to allow pulling in {@link JpaRepository} and {@link RevisionRepository} functionality in one
|
||||
* go.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface EnversRevisionRepository<T, ID extends Serializable, N extends Number & Comparable<N>> extends
|
||||
RevisionRepository<T, ID, N>, JpaRepository<T, ID> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 java.io.Serializable;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.hibernate.envers.DefaultRevisionEntity;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.history.RevisionRepository;
|
||||
import org.springframework.data.repository.history.support.RevisionEntityInformation;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} creating {@link RevisionRepository} instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class EnversRevisionRepositoryFactoryBean extends
|
||||
JpaRepositoryFactoryBean<EnversRevisionRepository<Object, Serializable, Long>, Object, Serializable> {
|
||||
|
||||
private Class<?> revisionEntityClass;
|
||||
|
||||
/**
|
||||
* Configures the revision entity class. Will default to {@link DefaultRevisionEntity}.
|
||||
*
|
||||
* @param revisionEntityClass
|
||||
*/
|
||||
public void setRevisionEntityClass(Class<?> revisionEntityClass) {
|
||||
this.revisionEntityClass = revisionEntityClass;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean#createRepositoryFactory(javax.persistence.EntityManager)
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
|
||||
return new RevisionRepositoryFactory(entityManager, revisionEntityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository factory creating {@link RevisionRepository} instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class RevisionRepositoryFactory extends JpaRepositoryFactory {
|
||||
|
||||
private final RevisionEntityInformation revisionEntityInformation;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RevisionRepositoryFactory} using the given {@link EntityManager} and revision entity class.
|
||||
*
|
||||
* @param entityManager must not be {@literal null}.
|
||||
* @param revisionEntityClass can be {@literal null}, will default to {@link DefaultRevisionEntity}.
|
||||
*/
|
||||
public RevisionRepositoryFactory(EntityManager entityManager, Class<?> revisionEntityClass) {
|
||||
|
||||
super(entityManager);
|
||||
revisionEntityClass = revisionEntityClass == null ? DefaultRevisionEntity.class : revisionEntityClass;
|
||||
this.revisionEntityInformation = DefaultRevisionEntity.class.equals(revisionEntityClass) ? new DefaultRevisionEntityInformation()
|
||||
: new ReflectionRevisionEntityInformation(revisionEntityClass);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata, javax.persistence.EntityManager)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
protected <T, ID extends Serializable> JpaRepository<?, ?> getTargetRepository(RepositoryMetadata metadata,
|
||||
EntityManager entityManager) {
|
||||
|
||||
JpaEntityInformation<T, Serializable> entityInformation = (JpaEntityInformation<T, Serializable>) getEntityInformation(metadata
|
||||
.getDomainClass());
|
||||
return new EnversRevisionRepositoryImpl(entityInformation, revisionEntityInformation, entityManager);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
|
||||
return EnversRevisionRepositoryImpl.class;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepository(java.lang.Class, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public <T> T getRepository(Class<T> repositoryInterface, Object customImplementation) {
|
||||
|
||||
if (RevisionRepository.class.isAssignableFrom(repositoryInterface)) {
|
||||
|
||||
Class<?>[] typeArguments = GenericTypeResolver.resolveTypeArguments(repositoryInterface,
|
||||
RevisionRepository.class);
|
||||
Class<?> revisionNumberType = typeArguments[2];
|
||||
|
||||
if (!revisionEntityInformation.getRevisionNumberType().equals(revisionNumberType)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Configured a revision entity type of %s with a revision type of %s "
|
||||
+ "but the repository interface is typed to a revision type of %s!", repositoryInterface,
|
||||
revisionEntityInformation.getRevisionNumberType(), revisionNumberType));
|
||||
}
|
||||
}
|
||||
|
||||
return super.getRepository(repositoryInterface, customImplementation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.hibernate.envers.AuditReader;
|
||||
import org.hibernate.envers.AuditReaderFactory;
|
||||
import org.hibernate.envers.DefaultRevisionEntity;
|
||||
import org.hibernate.envers.RevisionNumber;
|
||||
import org.hibernate.envers.RevisionTimestamp;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.history.AnnotationRevisionMetadata;
|
||||
import org.springframework.data.history.Revision;
|
||||
import org.springframework.data.history.RevisionMetadata;
|
||||
import org.springframework.data.history.Revisions;
|
||||
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
|
||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.history.RevisionRepository;
|
||||
import org.springframework.data.repository.history.support.RevisionEntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Repository implementation using Hibernate Envers to implement revision specific query methods.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Philipp Huegelmeyer
|
||||
*/
|
||||
public class EnversRevisionRepositoryImpl<T, ID extends Serializable, N extends Number & Comparable<N>> extends
|
||||
SimpleJpaRepository<T, ID> implements RevisionRepository<T, ID, N> {
|
||||
|
||||
private final EntityInformation<T, ?> entityInformation;
|
||||
private final RevisionEntityInformation revisionEntityInformation;
|
||||
private final EntityManager entityManager;
|
||||
|
||||
/**
|
||||
* Creates a new {@link EnversRevisionRepositoryImpl} using the given {@link JpaEntityInformation},
|
||||
* {@link RevisionEntityInformation} and {@link EntityManager}.
|
||||
*
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param revisionEntityInformation must not be {@literal null}.
|
||||
* @param entityManager must not be {@literal null}.
|
||||
*/
|
||||
public EnversRevisionRepositoryImpl(JpaEntityInformation<T, ?> entityInformation,
|
||||
RevisionEntityInformation revisionEntityInformation, EntityManager entityManager) {
|
||||
|
||||
super(entityInformation, entityManager);
|
||||
|
||||
Assert.notNull(revisionEntityInformation);
|
||||
|
||||
this.entityInformation = entityInformation;
|
||||
this.revisionEntityInformation = revisionEntityInformation;
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.RevisionRepository#findLastChangeRevision(java.io.Serializable)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Revision<N, T> findLastChangeRevision(ID id) {
|
||||
|
||||
Class<T> type = entityInformation.getJavaType();
|
||||
AuditReader reader = AuditReaderFactory.get(entityManager);
|
||||
|
||||
List<Number> revisions = reader.getRevisions(type, id);
|
||||
|
||||
if (revisions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
N latestRevision = (N) revisions.get(revisions.size() - 1);
|
||||
|
||||
Class<?> revisionEntityClass = revisionEntityInformation.getRevisionEntityClass();
|
||||
|
||||
Object revisionEntity = reader.findRevision(revisionEntityClass, latestRevision);
|
||||
RevisionMetadata<N> metadata = (RevisionMetadata<N>) getRevisionMetadata(revisionEntity);
|
||||
return new Revision<N, T>(metadata, reader.find(type, id, latestRevision));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.RevisionRepository#findRevisions(java.io.Serializable)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Revisions<N, T> findRevisions(ID id) {
|
||||
|
||||
Class<T> type = entityInformation.getJavaType();
|
||||
AuditReader reader = AuditReaderFactory.get(entityManager);
|
||||
List<? extends Number> revisionNumbers = reader.getRevisions(type, id);
|
||||
|
||||
return getEntitiesForRevisions((List<N>) revisionNumbers, id, reader);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.RevisionRepository#findRevisions(java.io.Serializable, org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Page<Revision<N, T>> findRevisions(ID id, Pageable pageable) {
|
||||
|
||||
Class<T> type = entityInformation.getJavaType();
|
||||
AuditReader reader = AuditReaderFactory.get(entityManager);
|
||||
List<Number> revisionNumbers = reader.getRevisions(type, id);
|
||||
|
||||
if (pageable.getOffset() > revisionNumbers.size()) {
|
||||
return new PageImpl<Revision<N, T>>(Collections.<Revision<N, T>> emptyList(), pageable, 0);
|
||||
}
|
||||
|
||||
int upperBound = pageable.getOffset() + pageable.getPageSize();
|
||||
upperBound = upperBound > revisionNumbers.size() ? revisionNumbers.size() : upperBound;
|
||||
|
||||
List<? extends Number> subList = revisionNumbers.subList(pageable.getOffset(), upperBound);
|
||||
Revisions<N, T> revisions = getEntitiesForRevisions((List<N>) subList, id, reader);
|
||||
|
||||
return new PageImpl<Revision<N, T>>(revisions.getContent(), pageable, revisionNumbers.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entities in the given revisions for the entitiy with the given id.
|
||||
*
|
||||
* @param revisionNumbers
|
||||
* @param id
|
||||
* @param reader
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Revisions<N, T> getEntitiesForRevisions(List<N> revisionNumbers, ID id, AuditReader reader) {
|
||||
|
||||
Class<T> type = entityInformation.getJavaType();
|
||||
Map<N, T> revisions = new HashMap<N, T>(revisionNumbers.size());
|
||||
|
||||
Class<?> revisionEntityClass = revisionEntityInformation.getRevisionEntityClass();
|
||||
Map<Number, Object> revisionEntities = (Map<Number, Object>) reader.findRevisions(revisionEntityClass,
|
||||
new HashSet<Number>(revisionNumbers));
|
||||
|
||||
for (Number number : revisionNumbers) {
|
||||
revisions.put((N) number, reader.find(type, id, number));
|
||||
}
|
||||
|
||||
return new Revisions<N, T>(toRevisions(revisions, revisionEntities));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Revision<N, T>> toRevisions(Map<N, T> source, Map<Number, Object> revisionEntities) {
|
||||
|
||||
List<Revision<N, T>> result = new ArrayList<Revision<N, T>>();
|
||||
|
||||
for (Entry<N, T> revision : source.entrySet()) {
|
||||
|
||||
N revisionNumber = revision.getKey();
|
||||
T entity = revision.getValue();
|
||||
RevisionMetadata<N> metadata = (RevisionMetadata<N>) getRevisionMetadata(revisionEntities.get(revisionNumber));
|
||||
result.add(new Revision<N, T>(metadata, entity));
|
||||
}
|
||||
|
||||
Collections.sort(result);
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RevisionMetadata} wrapper depending on the type of the given object.
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
private RevisionMetadata<?> getRevisionMetadata(Object object) {
|
||||
if (object instanceof DefaultRevisionEntity) {
|
||||
return new DefaultRevisionMetadata((DefaultRevisionEntity) object);
|
||||
} else {
|
||||
return new AnnotationRevisionMetadata<N>(object, RevisionNumber.class, RevisionTimestamp.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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.hibernate.envers.RevisionNumber;
|
||||
import org.springframework.data.repository.history.support.RevisionEntityInformation;
|
||||
import org.springframework.data.util.AnnotationDetectionFieldCallback;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link RevisionEntityInformation} that uses reflection to inspect a property annotated with {@link RevisionNumber} to
|
||||
* find out about the revision number type.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ReflectionRevisionEntityInformation implements RevisionEntityInformation {
|
||||
|
||||
private final Class<?> revisionEntityClass;
|
||||
private final Class<?> revisionNumberType;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReflectionRevisionEntityInformation} inspecting the given revision entity class.
|
||||
*
|
||||
* @param revisionEntityClass must not be {@literal null}.
|
||||
*/
|
||||
public ReflectionRevisionEntityInformation(Class<?> revisionEntityClass) {
|
||||
|
||||
Assert.notNull(revisionEntityClass);
|
||||
|
||||
AnnotationDetectionFieldCallback fieldCallback = new AnnotationDetectionFieldCallback(RevisionNumber.class);
|
||||
ReflectionUtils.doWithFields(revisionEntityClass, fieldCallback);
|
||||
|
||||
this.revisionNumberType = fieldCallback.getType();
|
||||
this.revisionEntityClass = revisionEntityClass;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionNumberType()
|
||||
*/
|
||||
public Class<?> getRevisionNumberType() {
|
||||
return revisionNumberType;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.support.RevisionEntityInformation#isDefaultRevisionEntity()
|
||||
*/
|
||||
public boolean isDefaultRevisionEntity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionEntityClass()
|
||||
*/
|
||||
public Class<?> getRevisionEntityClass() {
|
||||
return revisionEntityClass;
|
||||
}
|
||||
}
|
||||
76
src/test/java/org/springframework/data/envers/Config.java
Executable file
76
src/test/java/org/springframework/data/envers/Config.java
Executable file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.envers.strategy.ValidityAuditStrategy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Spring JavaConfig configuration for general infrastructure.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
public class Config {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() throws SQLException {
|
||||
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager() throws SQLException {
|
||||
|
||||
EntityManagerFactory entityManagerFactory = entityManagerFactoryBean().getObject();
|
||||
return new JpaTransactionManager(entityManagerFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AbstractEntityManagerFactoryBean entityManagerFactoryBean() throws SQLException {
|
||||
|
||||
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
|
||||
jpaVendorAdapter.setDatabase(Database.H2);
|
||||
jpaVendorAdapter.setGenerateDdl(true);
|
||||
|
||||
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
|
||||
bean.setJpaVendorAdapter(jpaVendorAdapter);
|
||||
bean.setPackagesToScan(Config.class.getPackage().getName());
|
||||
bean.setDataSource(dataSource());
|
||||
bean.setJpaPropertyMap(jpaProperties());
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
private Map<String, String> jpaProperties() {
|
||||
return Collections.singletonMap("org.hibernate.envers.audit_strategy", ValidityAuditStrategy.class.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.envers.Config;
|
||||
import org.springframework.data.envers.sample.Country;
|
||||
import org.springframework.data.envers.sample.CountryRepository;
|
||||
import org.springframework.data.envers.sample.License;
|
||||
import org.springframework.data.envers.sample.LicenseRepository;
|
||||
import org.springframework.data.history.Revision;
|
||||
import org.springframework.data.history.Revisions;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
/**
|
||||
* Integration tests for repositories.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
|
||||
public class RepositoryIntegrationTest {
|
||||
|
||||
@ImportResource("classpath:application-context.xml")
|
||||
@Configuration
|
||||
static class RepoConfig extends Config {
|
||||
|
||||
}
|
||||
|
||||
@Autowired
|
||||
LicenseRepository licenseRepository;
|
||||
@Autowired
|
||||
CountryRepository countryRepository;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
licenseRepository.deleteAll();
|
||||
countryRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testname() {
|
||||
|
||||
License license = new License();
|
||||
license.name = "Schnitzel";
|
||||
|
||||
licenseRepository.save(license);
|
||||
|
||||
Country de = new Country();
|
||||
de.code = "de";
|
||||
de.name = "Deutschland";
|
||||
|
||||
countryRepository.save(de);
|
||||
|
||||
Country se = new Country();
|
||||
se.code = "se";
|
||||
se.name = "Schweden";
|
||||
|
||||
countryRepository.save(se);
|
||||
|
||||
license.laender = new HashSet<Country>();
|
||||
license.laender.addAll(Arrays.asList(de, se));
|
||||
|
||||
licenseRepository.save(license);
|
||||
|
||||
de.name = "Daenemark";
|
||||
|
||||
countryRepository.save(de);
|
||||
|
||||
Revision<Integer, License> revision = licenseRepository.findLastChangeRevision(license.id);
|
||||
assertThat(revision, is(notNullValue()));
|
||||
|
||||
Page<Revision<Integer, License>> revisions = licenseRepository.findRevisions(license.id, new PageRequest(0, 10));
|
||||
Revisions<Integer, License> wrapper = new Revisions<Integer, License>(revisions.getContent());
|
||||
assertThat(wrapper.getLatestRevision(), is(revision));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@MappedSuperclass
|
||||
abstract class AbstractEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
public Long id;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof AbstractEntity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AbstractEntity that = (AbstractEntity) obj;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.id, that.id);
|
||||
}
|
||||
|
||||
}
|
||||
33
src/test/java/org/springframework/data/envers/sample/Country.java
Executable file
33
src/test/java/org/springframework/data/envers/sample/Country.java
Executable file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 javax.persistence.Entity;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
|
||||
/**
|
||||
* Sample domain class.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Audited
|
||||
@Entity
|
||||
public class Country extends AbstractEntity {
|
||||
|
||||
public String code;
|
||||
public String name;
|
||||
}
|
||||
28
src/test/java/org/springframework/data/envers/sample/CountryRepository.java
Executable file
28
src/test/java/org/springframework/data/envers/sample/CountryRepository.java
Executable file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.history.RevisionRepository;
|
||||
|
||||
/**
|
||||
* Repository for {@link Country} objects.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface CountryRepository extends RevisionRepository<Country, Long, Integer>, JpaRepository<Country, Long> {
|
||||
|
||||
}
|
||||
41
src/test/java/org/springframework/data/envers/sample/License.java
Executable file
41
src/test/java/org/springframework/data/envers/sample/License.java
Executable file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 java.util.Set;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
|
||||
/**
|
||||
* Sample domain class.
|
||||
*
|
||||
* @author Philip Huegelmeyer
|
||||
*/
|
||||
@Audited
|
||||
@Entity
|
||||
public class License extends AbstractEntity {
|
||||
|
||||
@Version
|
||||
public Integer version;
|
||||
|
||||
public String name;
|
||||
@ManyToMany
|
||||
public Set<Country> laender;
|
||||
}
|
||||
28
src/test/java/org/springframework/data/envers/sample/LicenseRepository.java
Executable file
28
src/test/java/org/springframework/data/envers/sample/LicenseRepository.java
Executable file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012 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
|
||||
*
|
||||
* http://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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.history.RevisionRepository;
|
||||
|
||||
/**
|
||||
* Repository for {@link License} objects.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface LicenseRepository extends RevisionRepository<License, Long, Integer>, JpaRepository<License, Long> {
|
||||
|
||||
}
|
||||
10
src/test/resources/application-context.xml
Executable file
10
src/test/resources/application-context.xml
Executable file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd">
|
||||
|
||||
<jpa:repositories base-package="org.springframework.data.envers" factory-class="org.springframework.data.envers.repository.support.EnversRevisionRepositoryFactoryBean" />
|
||||
|
||||
</beans>
|
||||
7
src/test/resources/log4j.properties
Executable file
7
src/test/resources/log4j.properties
Executable file
@@ -0,0 +1,7 @@
|
||||
log4j.rootCategory=WARN, console
|
||||
|
||||
log4j.appender.console.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.console.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n
|
||||
log4j.appender.console=org.apache.log4j.ConsoleAppender
|
||||
|
||||
log4j.logger.org.springframework=INFO
|
||||
Reference in New Issue
Block a user