net.croz.nrich.registry.history.service.DefaultRegistryHistoryService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nrich-registry Show documentation
Show all versions of nrich-registry Show documentation
Provides a representation of JPA entities in a format that can be used on the client side to build dynamic forms and tables,
as well as a REST API for querying, updating and deleting those entities
The newest version!
/*
* Copyright 2020-2023 CROZ d.o.o, 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 net.croz.nrich.registry.history.service;
import net.croz.nrich.registry.api.core.service.RegistryEntityFinderService;
import net.croz.nrich.registry.api.history.model.EntityWithRevision;
import net.croz.nrich.registry.api.history.model.RevisionInfo;
import net.croz.nrich.registry.api.history.request.ListRegistryHistoryRequest;
import net.croz.nrich.registry.api.history.service.RegistryHistoryService;
import net.croz.nrich.registry.core.constants.RegistryEnversConstants;
import net.croz.nrich.registry.core.model.PropertyWithType;
import net.croz.nrich.registry.core.model.RegistryDataConfiguration;
import net.croz.nrich.registry.core.model.RegistryDataConfigurationHolder;
import net.croz.nrich.registry.core.model.RegistryHistoryConfigurationHolder;
import net.croz.nrich.search.api.model.sort.SortDirection;
import net.croz.nrich.search.api.model.sort.SortProperty;
import net.croz.nrich.search.api.util.PageableUtil;
import net.croz.nrich.search.bean.MapSupportingDirectFieldAccessFallbackBeanWrapper;
import org.hibernate.envers.AuditReaderFactory;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.envers.query.AuditQuery;
import org.hibernate.envers.query.criteria.AuditProperty;
import org.modelmapper.ModelMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import jakarta.persistence.EntityManager;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.ManagedType;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class DefaultRegistryHistoryService implements RegistryHistoryService {
private final EntityManager entityManager;
private final RegistryDataConfigurationHolder registryDataConfigurationHolder;
private final RegistryHistoryConfigurationHolder registryHistoryConfigurationHolder;
private final ModelMapper modelMapper;
private final Map, ManagedType>> classManagedTypeMap;
private final RegistryEntityFinderService registryEntityFinderService;
public DefaultRegistryHistoryService(EntityManager entityManager, RegistryDataConfigurationHolder registryDataConfigurationHolder,
RegistryHistoryConfigurationHolder registryHistoryConfigurationHolder, ModelMapper modelMapper, RegistryEntityFinderService registryEntityFinderService) {
this.entityManager = entityManager;
this.registryDataConfigurationHolder = registryDataConfigurationHolder;
this.registryHistoryConfigurationHolder = registryHistoryConfigurationHolder;
this.modelMapper = modelMapper;
this.classManagedTypeMap = initializeManagedTypeMap(registryDataConfigurationHolder);
this.registryEntityFinderService = registryEntityFinderService;
}
@Transactional(readOnly = true)
public Page> historyList(ListRegistryHistoryRequest request) {
AuditQuery auditQuery = createAuditQuery(request);
addOrder(auditQuery, request.sortPropertyList());
List> resultList = auditQuery
.setFirstResult(request.pageNumber())
.setMaxResults(request.pageSize()).getResultList();
List> entityWithRevisionList = convertToEntityRevisionList(resultList);
Pageable pageable = PageableUtil.convertToPageable(request.pageNumber(), request.pageSize());
return PageableExecutionUtils.getPage(entityWithRevisionList, pageable, () -> executeCountQuery(createAuditQuery(request)));
}
private Map, ManagedType>> initializeManagedTypeMap(RegistryDataConfigurationHolder registryDataConfigurationHolder) {
if (registryDataConfigurationHolder.registryDataConfigurationList() == null) {
return Collections.emptyMap();
}
return registryDataConfigurationHolder.registryDataConfigurationList().stream()
.collect(Collectors.toMap(RegistryDataConfiguration::registryType, registryDataConfiguration -> entityManager.getMetamodel().managedType(registryDataConfiguration.registryType())));
}
private AuditQuery createAuditQuery(ListRegistryHistoryRequest request) {
@SuppressWarnings("unchecked")
Class type = (Class) registryDataConfigurationHolder.findRegistryConfigurationForClass(request.classFullName()).registryType();
AuditQuery auditQuery = AuditReaderFactory.get(entityManager).createQuery().forRevisionsOfEntity(type, false, true);
if (request.registryRecordId() != null) {
addIdCondition(type, auditQuery, request.registryRecordId());
}
return auditQuery;
}
private long executeCountQuery(AuditQuery auditQuery) {
auditQuery.addProjection(AuditEntity.revisionNumber().count());
return (Long) auditQuery.getSingleResult();
}
@SuppressWarnings("unchecked")
private List> convertToEntityRevisionList(List> resultList) {
List