This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch bugfix/FINERACT-1865 in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 7e1f736efcada9fc22c6f3e2ed40d756b7c7825f Author: Adam Saghy <[email protected]> AuthorDate: Wed Jan 25 09:40:59 2023 +0100 FINERACT-1865: Decrease optimistic lock exception probability --- .../domain/LoanAccountDomainService.java | 6 ++--- .../domain/LoanAccountDomainServiceJpa.java | 29 ++++++++++----------- .../domain/LoanTransactionRelation.java | 6 ----- ...nRescheduleRequestWritePlatformServiceImpl.java | 10 ++++---- .../LoanChargeWritePlatformServiceImpl.java | 9 ++++--- .../LoanWritePlatformServiceJpaRepositoryImpl.java | 30 ++++++++++------------ .../db/changelog/tenant/changelog-tenant.xml | 1 + ...0088_drop_m_loan_transaction_version_column.xml | 28 ++++++++++++++++++++ 8 files changed, 68 insertions(+), 51 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java index d41939658..d378ffc2c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java @@ -77,11 +77,11 @@ public interface LoanAccountDomainService { boolean isRecoveryRepayment, String chargeRefundChargeType, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, boolean isLoanToLoanTransfer); - void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction); + LoanTransaction saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction); - void saveAndFlushLoanWithDataIntegrityViolationChecks(Loan loan); + Loan saveAndFlushLoanWithDataIntegrityViolationChecks(Loan loan); - void saveLoanWithDataIntegrityViolationChecks(Loan loan); + Loan saveLoanWithDataIntegrityViolationChecks(Loan loan); LoanTransaction foreCloseLoan(Loan loan, LocalDate foreClourseDate, String noteText, ExternalId externalId, Map<String, Object> changes); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index d503a8e68..6b397732f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -156,11 +156,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { @Transactional @Override - public LoanTransaction makeRepayment(final LoanTransactionType repaymentTransactionType, final Loan loan, - final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, - final ExternalId txnExternalId, final boolean isRecoveryRepayment, final String chargeRefundChargeType, - boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, - final boolean isLoanToLoanTransfer) { + public LoanTransaction makeRepayment(final LoanTransactionType repaymentTransactionType, Loan loan, final LocalDate transactionDate, + final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, final ExternalId txnExternalId, + final boolean isRecoveryRepayment, final String chargeRefundChargeType, boolean isAccountTransfer, + HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, final boolean isLoanToLoanTransfer) { checkClientOrGroupActive(loan); LoanBusinessEvent repaymentEvent = getLoanRepaymentTypeBusinessEvent(repaymentTransactionType, isRecoveryRepayment, loan); @@ -206,13 +205,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { * time being, not a major issue for now as this loop is entered only in edge cases (when a payment is made * before the latest payment recorded against the loan) ***/ - - saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - if (changedTransactionDetail != null) { for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { - saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue()); + loanTransactionRepository.save(mapEntry.getValue()); // update loan with references to the newly created transactions loan.addLoanTransaction(mapEntry.getValue()); updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); @@ -220,6 +216,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); if (StringUtils.isNotBlank(noteText)) { final Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText); @@ -314,9 +311,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } @Override - public void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) { + public LoanTransaction saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) { try { - this.loanTransactionRepository.saveAndFlush(newRepaymentTransaction); + return this.loanTransactionRepository.saveAndFlush(newRepaymentTransaction); } catch (final JpaSystemException | DataIntegrityViolationException e) { raiseValidationExceptionForUniqueConstraintViolation(e); throw e; @@ -324,9 +321,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } @Override - public void saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) { + public Loan saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) { try { - this.loanRepositoryWrapper.saveAndFlush(loan); + return this.loanRepositoryWrapper.saveAndFlush(loan); } catch (final JpaSystemException | DataIntegrityViolationException e) { raiseValidationExceptionForUniqueConstraintViolation(e); throw e; @@ -334,9 +331,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } @Override - public void saveLoanWithDataIntegrityViolationChecks(final Loan loan) { + public Loan saveLoanWithDataIntegrityViolationChecks(final Loan loan) { try { - this.loanRepositoryWrapper.save(loan); + return this.loanRepositoryWrapper.save(loan); } catch (final JpaSystemException | DataIntegrityViolationException e) { raiseValidationExceptionForUniqueConstraintViolation(e); throw e; @@ -795,7 +792,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { if (changedTransactionDetail != null) { for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { - saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue()); + loanTransactionRepository.save(mapEntry.getValue()); // update loan with references to the newly created transactions loan.getLoanTransactions().add(mapEntry.getValue()); updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelation.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelation.java index 6f57e73a0..b98dae08a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelation.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelation.java @@ -25,14 +25,11 @@ import javax.persistence.Enumerated; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; -import javax.persistence.Version; import javax.validation.constraints.NotNull; import lombok.Getter; -import lombok.Setter; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; @Getter -@Setter @Entity @Table(name = "m_loan_transaction_relation") public class LoanTransactionRelation extends AbstractAuditableWithUTCDateTimeCustom { @@ -53,9 +50,6 @@ public class LoanTransactionRelation extends AbstractAuditableWithUTCDateTimeCus @Column(name = "relation_type_enum", nullable = false) private LoanTransactionRelationTypeEnum relationType; - @Version - private Long version; - protected LoanTransactionRelation() {} protected LoanTransactionRelation(@NotNull LoanTransaction fromTransaction, LoanTransaction toTransaction, LoanCharge toCharge, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java index 6396908d0..3c3a62e7e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java @@ -443,9 +443,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche // update the status of the request loanRescheduleRequest.approve(appUser, approvedOnDate); - // update the loan object - saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - if (changedTransactionDetail != null) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); @@ -457,6 +454,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + // update the loan object + loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); this.loanAccountDomainService.recalculateAccruals(loan, true); @@ -475,7 +474,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche } } - private void saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) { + private Loan saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) { try { List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments(); for (LoanRepaymentScheduleInstallment installment : installments) { @@ -483,7 +482,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche this.repaymentScheduleInstallmentRepository.save(installment); } } - this.loanRepositoryWrapper.saveAndFlush(loan); + return this.loanRepositoryWrapper.saveAndFlush(loan); } catch (final JpaSystemException | DataIntegrityViolationException e) { final Throwable realCause = e.getCause(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); @@ -495,6 +494,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors, e); } + throw e; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java index e755b481d..541d7d37f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java @@ -165,7 +165,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo this.loanChargeApiJsonValidator.validateAddLoanCharge(command.json()); - final Loan loan = this.loanAssembler.assembleFrom(loanId); + Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); List<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails(); @@ -248,8 +248,9 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } - this.loanRepositoryWrapper.save(loan); + } + loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); @@ -936,7 +937,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo } } - private boolean addCharge(final Loan loan, final Charge chargeDefinition, final LoanCharge loanCharge) { + private boolean addCharge(final Loan loan, final Charge chargeDefinition, LoanCharge loanCharge) { if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) { final String errorMessage = "Charge and Loan must have the same currency."; @@ -970,7 +971,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo loan.addLoanCharge(loanCharge); - this.loanChargeRepository.saveAndFlush(loanCharge); + loanCharge = this.loanChargeRepository.saveAndFlush(loanCharge); /** * we want to apply charge transactions only for those loans charges that are applied when a loan is active and diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 5b0aca276..199a24b67 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -448,15 +448,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan.adjustNetDisbursalAmount(amountToDisburse.getAmount()); } if (!changes.isEmpty()) { - - loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - - final String noteText = command.stringValueOfParameterNamed("note"); - if (StringUtils.isNotBlank(noteText)) { - final Note note = Note.loanNote(loan, noteText); - this.noteRepository.save(note); - } - if (changedTransactionDetail != null) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); @@ -466,6 +457,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); + + final String noteText = command.stringValueOfParameterNamed("note"); + if (StringUtils.isNotBlank(noteText)) { + final Note note = Note.loanNote(loan, noteText); + this.noteRepository.save(note); + } // auto create standing instruction createStandingInstruction(loan); @@ -729,8 +727,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } if (!changes.isEmpty()) { - loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { final Note note = Note.loanNote(loan, noteText); @@ -745,6 +741,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } final Set<LoanCharge> loanCharges = loan.getActiveCharges(); @@ -1136,7 +1133,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf * time being, not a major issue for now as this loop is entered only in edge cases (when a adjustment is made * before the latest payment recorded against the loan) ***/ - loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); if (changedTransactionDetail != null) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); @@ -1147,6 +1143,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { @@ -1370,7 +1367,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf * time being, not a major issue for now as this loop is entered only in edge cases (when a waiver is made * before the latest payment recorded against the loan) ***/ - loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); + if (changedTransactionDetail != null) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); @@ -1381,6 +1378,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { @@ -2290,8 +2288,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } - loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - if (command.entityId() != null && changedTransactionDetail != null) { for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); @@ -2300,6 +2296,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { createLoanScheduleArchive(loan, scheduleGeneratorDTO); } @@ -2345,8 +2342,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(generatorDTO, existingTransactionIds, existingReversedTransactionIds); - saveLoanWithDataIntegrityViolationChecks(loan); - if (changedTransactionDetail != null) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { this.loanTransactionRepository.save(mapEntry.getValue()); @@ -2358,6 +2353,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // Trigger transaction replayed event replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); } + saveLoanWithDataIntegrityViolationChecks(loan); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); loanAccountDomainService.recalculateAccruals(loan); businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan)); diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 1812cb54d..225537762 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -107,4 +107,5 @@ <include file="parts/0085_add_aggregate_root_id_external_events.xml" relativeToChangelogFile="true" /> <include file="parts/0086_add_cob_business_date_to_loan_account_locks.xml" relativeToChangelogFile="true" /> <include file="parts/0087_update_dashboard_table_reports.xml" relativeToChangelogFile="true" /> + <include file="parts/0088_drop_m_loan_transaction_version_column.xml" relativeToChangelogFile="true" /> </databaseChangeLog> diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0088_drop_m_loan_transaction_version_column.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0088_drop_m_loan_transaction_version_column.xml new file mode 100644 index 000000000..19dbb287d --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0088_drop_m_loan_transaction_version_column.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + +--> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd"> + <changeSet author="fineract" id="1"> + <dropColumn tableName="m_loan_transaction_relation" columnName="version"/> + </changeSet> +</databaseChangeLog>
