Created
October 5, 2016 04:45
-
-
Save tanapoln/af4b4a40e5472f0c8e9625d31f7d2153 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 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.QueryDslJpaRepository; | |
import org.springframework.data.querydsl.EntityPathResolver; | |
import org.springframework.data.repository.core.EntityInformation; | |
import org.springframework.data.repository.history.support.RevisionEntityInformation; | |
import org.springframework.util.Assert; | |
/** | |
* @author Tanapol Nearunchorn | |
*/ | |
public class EnversQueryDslRepositoryImpl<T, ID extends Serializable, N extends Number & Comparable<N>> | |
extends QueryDslJpaRepository<T, ID> | |
implements EnversRevisionRepository<T, ID, N> { | |
private final EntityInformation<T, ?> entityInformation; | |
private final RevisionEntityInformation revisionEntityInformation; | |
private final EntityManager entityManager; | |
/** | |
* Constructor. | |
* | |
* @param entityInformation | |
* entity information | |
* @param revisionEntityInformation | |
* revision entity information | |
* @param entityManager | |
* entity manager | |
*/ | |
public EnversQueryDslRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, | |
RevisionEntityInformation revisionEntityInformation, | |
EntityManager entityManager) { | |
super(entityInformation, entityManager); | |
Assert.notNull(revisionEntityInformation); | |
this.entityInformation = entityInformation; | |
this.revisionEntityInformation = revisionEntityInformation; | |
this.entityManager = entityManager; | |
} | |
/** | |
* Another Constructor. | |
* | |
* @param entityInformation | |
* entity information | |
* @param entityManager | |
* entity manager | |
* @param revisionEntityInformation | |
* revision entity information | |
* @param resolver | |
* resolver | |
*/ | |
public EnversQueryDslRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager, | |
RevisionEntityInformation revisionEntityInformation, | |
EntityPathResolver resolver) { | |
super(entityInformation, entityManager, resolver); | |
Assert.notNull(revisionEntityInformation); | |
this.entityInformation = entityInformation; | |
this.entityManager = entityManager; | |
this.revisionEntityInformation = revisionEntityInformation; | |
} | |
/* | |
* (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.envers.repository.support.EnversRevisionRepository#findRevision(java.io.Serializable, java.lang.Number) | |
*/ | |
@Override | |
public Revision<N, T> findRevision(ID id, N revisionNumber) { | |
Assert.notNull(id, "Identifier must not be null!"); | |
Assert.notNull(revisionNumber, "Revision number must not be null!"); | |
return getEntityForRevision(revisionNumber, id, AuditReaderFactory.get(entityManager)); | |
} | |
/* | |
* (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 revisionNumbers.isEmpty() ? new Revisions<N, T>(Collections.EMPTY_LIST) : 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)); | |
} | |
/** | |
* Returns an entity in the given revision for the given entity-id. | |
* | |
* @param revisionNumber | |
* @param id | |
* @param reader | |
* @return | |
*/ | |
@SuppressWarnings("unchecked") | |
private Revision<N, T> getEntityForRevision(N revisionNumber, ID id, AuditReader reader) { | |
Class<?> type = revisionEntityInformation.getRevisionEntityClass(); | |
T revision = (T) reader.findRevision(type, revisionNumber); | |
Object entity = reader.find(entityInformation.getJavaType(), id, revisionNumber); | |
return new Revision<N, T>((RevisionMetadata<N>) getRevisionMetadata(revision), (T) entity); | |
} | |
@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 (Map.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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.data.envers.repository.support; | |
import java.io.Serializable; | |
import javax.persistence.EntityManager; | |
import org.hibernate.envers.DefaultRevisionEntity; | |
import org.springframework.core.GenericTypeResolver; | |
import org.springframework.data.jpa.repository.support.JpaEntityInformation; | |
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; | |
import org.springframework.data.repository.core.RepositoryInformation; | |
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; | |
/** | |
* @author Tanapol Nearunchorn | |
*/ | |
public class EnversRevisionQueryDslRepositoryFactoryBean extends EnversRevisionRepositoryFactoryBean { | |
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<T, ID extends Serializable, N extends Number & Comparable<N>> | |
extends JpaRepositoryFactory { | |
private final RevisionEntityInformation revisionEntityInformation; | |
private final EntityManager entityManager; | |
/** | |
* 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); | |
this.entityManager = 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 EnversQueryDslRepositoryImpl getTargetRepository(RepositoryInformation information) { | |
JpaEntityInformation<T, ID> entityInformation = (JpaEntityInformation<T, ID>) getEntityInformation( | |
information.getDomainType()); | |
return new EnversQueryDslRepositoryImpl<T, ID, N>(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 EnversQueryDslRepositoryImpl.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); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment