Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FINERACT-2022: Rewrite account domain, services and jobs using QueryDSL. #3854

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
6 changes: 6 additions & 0 deletions fineract-core/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,10 @@ dependencies {
implementation(
project(path: ':fineract-avro-schemas')
)
implementation('io.github.openfeign.querydsl:querydsl-core:6.2.1',
'io.github.openfeign.querydsl:querydsl-jpa:6.2.1')
annotationProcessor(
'io.github.openfeign.querydsl:querydsl-apt:6.2.1:jakarta',
'jakarta.persistence:jakarta.persistence-api:3.1.0',
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public static BigDecimal getBigDecimalDefaultToNullIfZero(final ResultSet rs, fi
return defaultToNullIfZero(value);
}

private static BigDecimal defaultToNullIfZero(final BigDecimal value) {
public static BigDecimal defaultToNullIfZero(final BigDecimal value) {
BigDecimal result = value;
if (value != null && BigDecimal.ZERO.compareTo(value) == 0) {
result = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public <E> Page<E> fetchPage(final JdbcTemplate jt, final String sqlFetchRows, f
return new Page<>(items, totalFilteredRecords);
}

public <E> Page<E> createPageFromItems(final List<E> items, final Long filteredRecords) {
return new Page<>(items, filteredRecords.intValue());
}

public <E> Page<Long> fetchPage(JdbcTemplate jdbcTemplate, String sql, Class<Long> type) {
final List<Long> items = jdbcTemplate.queryForList(sql, type);

Expand Down
6 changes: 6 additions & 0 deletions fineract-document/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ dependencies {
implementation('org.eclipse.persistence:org.eclipse.persistence.jpa') {
exclude group: 'org.eclipse.persistence', module: 'jakarta.persistence'
}
implementation('io.github.openfeign.querydsl:querydsl-core:6.2.1',
'io.github.openfeign.querydsl:querydsl-jpa:6.2.1')
annotationProcessor(
'io.github.openfeign.querydsl:querydsl-apt:6.2.1:jakarta',
'jakarta.persistence:jakarta.persistence-api:3.1.0',
)
// testCompile dependencies are ONLY used in src/test, not src/main.
// Do NOT repeat dependencies which are ALREADY in implementation or runtimeOnly!
//
Expand Down
6 changes: 6 additions & 0 deletions fineract-loan/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ dependencies {
implementation('org.eclipse.persistence:org.eclipse.persistence.jpa') {
exclude group: 'org.eclipse.persistence', module: 'jakarta.persistence'
}
implementation('io.github.openfeign.querydsl:querydsl-core:6.2.1',
'io.github.openfeign.querydsl:querydsl-jpa:6.2.1')
annotationProcessor(
'io.github.openfeign.querydsl:querydsl-apt:6.2.1:jakarta',
'jakarta.persistence:jakarta.persistence-api:3.1.0',
)
// testCompile dependencies are ONLY used in src/test, not src/main.
// Do NOT repeat dependencies which are ALREADY in implementation or runtimeOnly!
//
Expand Down
7 changes: 7 additions & 0 deletions fineract-provider/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ dependencies {

implementation 'io.github.classgraph:classgraph'

implementation('io.github.openfeign.querydsl:querydsl-core:6.2.1',
'io.github.openfeign.querydsl:querydsl-jpa:6.2.1')
annotationProcessor(
'io.github.openfeign.querydsl:querydsl-apt:6.2.1:jakarta',
'jakarta.persistence:jakarta.persistence-api:3.1.0',
)

// testCompile dependencies are ONLY used in src/test, not src/main.
// Do NOT repeat dependencies which are ALREADY in implementation or runtimeOnly!
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.account.domain;

import java.util.Collection;
import org.apache.fineract.portfolio.account.data.AccountAssociationsData;

public interface AccountAssociationsCustomRepository {

AccountAssociationsData retrieveLoanLinkedAssociation(Long loanId);

AccountAssociationsData retrieveSavingsLinkedAssociation(Long savingsId);

Collection<AccountAssociationsData> retrieveLoanAssociations(Long loanId, Integer associationType);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.account.domain;

import static java.util.stream.Collectors.toList;

import com.querydsl.core.Tuple;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.SimpleExpression;
import com.querydsl.jpa.impl.JPAQuery;
import jakarta.persistence.EntityManager;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.portfolio.account.data.AccountAssociationsData;
import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.loanaccount.domain.QLoan;
import org.apache.fineract.portfolio.savings.domain.QSavingsAccount;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class AccountAssociationsCustomRepositoryImpl implements AccountAssociationsCustomRepository {

private final EntityManager entityManager;

@Override
public AccountAssociationsData retrieveLoanLinkedAssociation(final Long loanId) {
final QAccountAssociations qAccountAssociations = QAccountAssociations.accountAssociations;
final JPAQuery<Tuple> query = getAccountAssociationsSelectQuery();
final Tuple queryResult = query
.where(eq(qAccountAssociations.loanAccount.id, loanId)
.and(qAccountAssociations.associationType.eq(AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue())))
.fetchOne();

return queryResult != null ? mapQueryResultToAccountAssociationsData(queryResult) : null;
}

@Override
public AccountAssociationsData retrieveSavingsLinkedAssociation(final Long savingsId) {
final QAccountAssociations qAccountAssociations = QAccountAssociations.accountAssociations;
final JPAQuery<Tuple> query = getAccountAssociationsSelectQuery();
final Tuple queryResult = query
.where(eq(qAccountAssociations.savingsAccount.id, savingsId)
.and(qAccountAssociations.associationType.eq(AccountAssociationType.LINKED_ACCOUNT_ASSOCIATION.getValue())))
.fetchOne();

return queryResult != null ? mapQueryResultToAccountAssociationsData(queryResult) : null;
}

@Override
public Collection<AccountAssociationsData> retrieveLoanAssociations(final Long loanId, final Integer associationType) {
final QAccountAssociations qAccountAssociations = QAccountAssociations.accountAssociations;
final JPAQuery<Tuple> query = getAccountAssociationsSelectQuery();
final List<Tuple> queryResult = query
.where(eq(qAccountAssociations.loanAccount.id, loanId).and(eq(qAccountAssociations.associationType, associationType)))
.fetch();
return queryResult.isEmpty() ? Collections.emptyList() : mapQueryResultToAccountAssociationsDataList(queryResult);
}

private JPAQuery<Tuple> getAccountAssociationsSelectQuery() {
final QAccountAssociations qAccountAssociations = QAccountAssociations.accountAssociations;
final QLoan qLoan = QLoan.loan;
final QSavingsAccount qSavingsAccount = QSavingsAccount.savingsAccount;

final JPAQuery<Tuple> query = new JPAQuery<>(entityManager);
query.select(qAccountAssociations.id, qLoan.id.as("loanAccountId"), qLoan.accountNumber.as("loanAccountNo"),
qSavingsAccount.id.as("linkSavingsAccountId"), qSavingsAccount.accountNumber.as("linkSavingsAccountNo"))
.from(qAccountAssociations).leftJoin(qAccountAssociations.loanAccount, qLoan)
.on(qLoan.id.eq(qAccountAssociations.loanAccount.id)).leftJoin(qAccountAssociations.linkedSavingsAccount, qSavingsAccount)
.on(qSavingsAccount.id.eq(qAccountAssociations.linkedSavingsAccount.id));

return query;
}

private AccountAssociationsData mapQueryResultToAccountAssociationsData(final Tuple queryResult) {
final QAccountAssociations qAccountAssociations = QAccountAssociations.accountAssociations;
final QLoan qLoan = QLoan.loan;
final QSavingsAccount qSavingsAccount = QSavingsAccount.savingsAccount;

final PortfolioAccountData account = PortfolioAccountData.lookup(queryResult.get(qLoan.id.as("loanAccountId")),
queryResult.get(qLoan.accountNumber.as("loanAccountNo")));
final PortfolioAccountData linkedAccount = PortfolioAccountData.lookup(
queryResult.get(qSavingsAccount.id.as("linkSavingsAccountId")),
queryResult.get(qSavingsAccount.accountNumber.as("linkSavingsAccountNo")));

return new AccountAssociationsData(queryResult.get(qAccountAssociations.id), account, linkedAccount);
}

private List<AccountAssociationsData> mapQueryResultToAccountAssociationsDataList(final List<Tuple> queryResult) {
return queryResult.stream().map(this::mapQueryResultToAccountAssociationsData).collect(toList());
}

private <T> BooleanExpression eq(final SimpleExpression<T> expression, final T value) {
return value == null ? expression.isNull() : expression.eq(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,6 @@

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;

public interface AccountAssociationsRepository
extends JpaRepository<AccountAssociations, Long>, JpaSpecificationExecutor<AccountAssociations> {

@Query("select aa from AccountAssociations aa where aa.loanAccount.id= :loanId and aa.associationType = :associationType")
AccountAssociations findByLoanIdAndType(@Param("loanId") Long loanId, @Param("associationType") Integer accountAssociationType);

@Query("select aa from AccountAssociations aa where aa.savingsAccount.id= :savingsId and aa.associationType = :associationType")
AccountAssociations findBySavingsIdAndType(@Param("savingsId") Long savingsId,
@Param("associationType") Integer accountAssociationType);
}
extends JpaRepository<AccountAssociations, Long>, JpaSpecificationExecutor<AccountAssociations> {}
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,9 @@
*/
package org.apache.fineract.portfolio.account.domain;

import java.util.Collection;
import java.util.List;
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.data.querydsl.QuerydslPredicateExecutor;

public interface AccountTransferRepository
extends JpaRepository<AccountTransferTransaction, Long>, JpaSpecificationExecutor<AccountTransferTransaction> {

@Query("select att from AccountTransferTransaction att where att.accountTransferDetails.fromLoanAccount.id= :accountNumber and att.reversed=false")
List<AccountTransferTransaction> findByFromLoanId(@Param("accountNumber") Long accountNumber);

@Query("select att from AccountTransferTransaction att where (att.accountTransferDetails.fromLoanAccount.id= :accountNumber or att.accountTransferDetails.toLoanAccount.id=:accountNumber) and att.reversed=false order by att.id desc")
List<AccountTransferTransaction> findAllByLoanId(@Param("accountNumber") Long accountNumber);

@Query("select att from AccountTransferTransaction att where att.toLoanTransaction.id= :loanTransactionId and att.reversed=false")
AccountTransferTransaction findByToLoanTransactionId(@Param("loanTransactionId") Long loanTransactionId);

@Query("select att from AccountTransferTransaction att where att.fromLoanTransaction.id IN :loanTransactions and att.reversed=false")
List<AccountTransferTransaction> findByFromLoanTransactions(@Param("loanTransactions") Collection<Long> loanTransactions);
}
public interface AccountTransferRepository extends JpaRepository<AccountTransferTransaction, Long>,
JpaSpecificationExecutor<AccountTransferTransaction>, QuerydslPredicateExecutor<AccountTransferTransaction> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.account.domain;

import java.time.LocalDate;

public interface AccountTransferStandingInstructionCustomRepository {

void updateLastRunDateById(long id, LocalDate lastRunDate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.account.domain;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class AccountTransferStandingInstructionCustomRepositoryImpl implements AccountTransferStandingInstructionCustomRepository {

private final EntityManager entityManager;

@Override
public void updateLastRunDateById(final long id, final LocalDate lastRunDate) {
final JPAQueryFactory queryFactory = new JPAQueryFactory(this.entityManager);
final QAccountTransferStandingInstruction qAccountTransferStandingInstruction = QAccountTransferStandingInstruction.accountTransferStandingInstruction;

queryFactory.update(qAccountTransferStandingInstruction).set(qAccountTransferStandingInstruction.latsRunDate, lastRunDate)
.where(qAccountTransferStandingInstruction.id.eq(id)).execute();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* 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.
*/
package org.apache.fineract.portfolio.account.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "m_account_transfer_standing_instructions_history")
public class AccountTransferStandingInstructionsHistory extends AbstractPersistableCustom {

@ManyToOne
@JoinColumn(name = "standing_instruction_id")
private AccountTransferStandingInstruction accountTransferStandingInstruction;

@Column(name = "status", length = 20)
private String status;

@Column(name = "execution_time")
private LocalDateTime executionTime;

@Column(name = "amount", scale = 6, precision = 19)
private BigDecimal amount;

@Column(name = "error_log", length = 500)
private String errorLog;
}
Loading