FINERACT-2234: add extra caches to frequent database queries during the interest recalculation

This commit is contained in:
budaidev
2025-04-28 14:51:11 +02:00
committed by Adam Saghy
parent efd17d5a3e
commit 6d3e19cf25
7 changed files with 382 additions and 10 deletions
@@ -25,15 +25,23 @@ import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
@CacheConfig(cacheNames = "calendarInstances")
public interface CalendarInstanceRepository extends JpaRepository<CalendarInstance, Long>, JpaSpecificationExecutor<CalendarInstance> {
@Cacheable(key = "'calId_' + #calendarId + '_entityId_' + #entityId + '_entityTypeId_' + #entityTypeId")
CalendarInstance findByCalendarIdAndEntityIdAndEntityTypeId(Long calendarId, Long entityId, Integer entityTypeId);
@Cacheable(key = "'entityId_' + #entityId + '_entityTypeId_' + #entityTypeId")
Collection<CalendarInstance> findByEntityIdAndEntityTypeId(Long entityId, Integer entityTypeId);
/**
@@ -45,14 +53,18 @@ public interface CalendarInstanceRepository extends JpaRepository<CalendarInstan
* {@link CalendarType}
* @return
*/
@Cacheable(key = "'entityId_' + #entityId + '_entityTypeId_' + #entityTypeId + '_calendarTypeId_' + #calendarTypeId")
CalendarInstance findByEntityIdAndEntityTypeIdAndCalendarTypeId(Long entityId, Integer entityTypeId, Integer calendarTypeId);
@Cacheable(key = "'findCalendarInstanceByEntityId_entityId_' + #entityId + '_entityTypeId_' + #entityTypeId")
@Query("select ci from CalendarInstance ci where ci.entityId = :entityId and ci.entityTypeId = :entityTypeId")
CalendarInstance findCalendarInstaneByEntityId(@Param("entityId") Long entityId, @Param("entityTypeId") Integer entityTypeId);
CalendarInstance findCalendarInstanceByEntityId(@Param("entityId") Long entityId, @Param("entityTypeId") Integer entityTypeId);
@Cacheable(key = "'calendarId_' + #calendarId + '_entityTypeId_' + #entityTypeId")
Collection<CalendarInstance> findByCalendarIdAndEntityTypeId(Long calendarId, Integer entityTypeId);
/** Should use in clause, can I do it without creating a new class? **/
@Cacheable(key = "'groupId_' + #groupId + '_clientId_' + #clientId + '_statuses_' + T(org.springframework.util.StringUtils).collectionToCommaDelimitedString(#loanStatuses)")
@Query("select ci from CalendarInstance ci where ci.entityId in (select loan.id from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId and loan.loanStatus in :loanStatuses) and ci.entityTypeId = 3")
List<CalendarInstance> findCalendarInstancesForLoansByGroupIdAndClientIdAndStatuses(@Param("groupId") Long groupId,
@Param("clientId") Long clientId, @Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@@ -60,9 +72,41 @@ public interface CalendarInstanceRepository extends JpaRepository<CalendarInstan
/**
* EntityType = 3 is for loan
*/
@Cacheable(key = "'countLoans_calendarId_' + #calendarId + '_statuses_' + T(org.springframework.util.StringUtils).collectionToCommaDelimitedString(#loanStatuses)")
@Query("SELECT COUNT(ci.id) FROM CalendarInstance ci, Loan loan WHERE loan.id = ci.entityId AND ci.entityTypeId = 3 AND ci.calendar.id = :calendarId AND loan.loanStatus IN :loanStatuses ")
Integer countOfLoansSyncedWithCalendar(@Param("calendarId") Long calendarId,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
// Override JpaRepository methods to add cache eviction
@Override
@CacheEvict(allEntries = true)
<S extends CalendarInstance> S save(S entity);
@Override
@CacheEvict(allEntries = true)
<S extends CalendarInstance> List<S> saveAll(Iterable<S> entities);
@Override
@CacheEvict(allEntries = true)
void delete(CalendarInstance entity);
@Override
@CacheEvict(allEntries = true)
void deleteById(Long id);
@Override
@CacheEvict(allEntries = true)
void deleteAll();
@Override
@CacheEvict(allEntries = true)
void deleteAll(Iterable<? extends CalendarInstance> entities);
@Override
@CacheEvict(allEntries = true)
void deleteAllById(Iterable<? extends Long> ids);
@Override
@CacheEvict(allEntries = true)
<S extends CalendarInstance> S saveAndFlush(S entity);
}
@@ -916,7 +916,7 @@ public class LoanScheduleAssembler {
overlappings);
}
LoanProductVariableInstallmentConfig installmentConfig = loan.loanProduct().loanProductVariableInstallmentConfig();
final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
Calendar loanCalendar = null;
if (loanCalendarInstance != null) {
@@ -2164,7 +2164,7 @@ public final class LoanApplicationValidator {
}
private Calendar getCalendarInstance(Loan loan) {
CalendarInstance calendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
CalendarInstance calendarInstance = calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
return calendarInstance != null ? calendarInstance.getCalendar() : null;
}
@@ -195,7 +195,7 @@ public final class LoanTransactionValidator {
StatusEnum.DISBURSE.getValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
if (loan.isSyncDisbursementWithMeeting()) {
validateDisbursementDateWithMeetingDate(actualDisbursementDate, calendarInstance,
@@ -78,7 +78,7 @@ public class LoanUtilService {
}
final MonetaryCurrency currency = loan.getCurrency();
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
Calendar calendar = null;
CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
@@ -93,10 +93,10 @@ public class LoanUtilService {
CalendarInstance compoundingCalendarInstance = null;
Long overdurPenaltyWaitPeriod = null;
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
restCalendarInstance = calendarInstanceRepository.findCalendarInstanceByEntityId(loan.loanInterestRecalculationDetailId(),
CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstanceByEntityId(
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
}
final Boolean isInterestChargedFromDateAsDisbursementDateEnabled = this.configurationDomainService
@@ -156,7 +156,7 @@ public class LoanUtilService {
}
public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) {
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
CalendarEntityType.LOANS.getValue());
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
return this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance, calendarHistoryDataWrapper);