FINERACT-1865: Decrease optimistic lock exception probability

This commit is contained in:
Adam Saghy
2023-01-25 12:22:27 +01:00
parent cd0cec0229
commit 1733eacf3a
8 changed files with 68 additions and 51 deletions
@@ -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);
@@ -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());
@@ -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,
@@ -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;
}
}
@@ -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
@@ -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));
@@ -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>
@@ -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>