From 8389106a58df6f3b1f16bb2795ffee49b3e6ec61 Mon Sep 17 00:00:00 2001 From: Arnold Galovics Date: Tue, 22 Feb 2022 17:16:56 +0100 Subject: [PATCH] FINERACT-984-6: PostgreSQL support --- docker-compose-postgresql.yml | 107 + docker-compose.yml | 2 +- fineract-db/docker/postgresql/01-init.sh | 29 + fineract-provider/build.gradle | 3 + .../JournalEntryReadPlatformServiceImpl.java | 42 +- ...lEntryRunningBalanceUpdateServiceImpl.java | 50 +- ...sioningEntriesReadPlatformServiceImpl.java | 36 +- .../fineract/adhocquery/domain/AdHoc.java | 2 +- .../service/AdHocReadPlatformServiceImpl.java | 18 +- .../service/AuditReadPlatformServiceImpl.java | 14 +- .../campaigns/email/domain/EmailCampaign.java | 2 +- .../EmailCampaignReadPlatformServiceImpl.java | 2 +- .../service/EmailReadPlatformServiceImpl.java | 26 +- .../SmsCampaignReadPlatformServiceImpl.java | 30 +- .../core/config/PersistenceConfig.java | 5 +- .../core/service/PaginationHelper.java | 30 +- .../DatabaseIndependentQueryService.java | 10 +- .../database/DatabaseQueryService.java | 6 +- .../DatabaseSpecificSQLGenerator.java | 150 ++ .../core/service/database/DatabaseType.java | 2 +- .../database/DatabaseTypeResolver.java | 56 +- .../service/database/MySQLQueryService.java | 28 +- .../database/PostgreSQLQueryService.java | 65 + ...DatabaseAwareMigrationContextProvider.java | 7 +- .../ExtendedSpringLiquibaseFactory.java | 2 +- .../CreditReportReadPlatformServiceImpl.java | 2 +- .../creditbureau/domain/CreditBureau.java | 2 + .../CreditBureauLoanProductMapping.java | 7 +- .../domain/CreditBureauToken.java | 6 +- .../creditbureau/domain/CreditReport.java | 6 +- .../domain/OrganisationCreditBureau.java | 2 + ...ProductMappingReadPlatformServiceImpl.java | 2 +- .../CreditBureauReadPlatformServiceImpl.java | 2 +- ...onCreditBureauReadPlatformServiceImpl.java | 2 +- .../data/ResultsetColumnHeaderData.java | 9 +- ...atatableChecksReadPlatformServiceImpl.java | 22 +- .../service/GenericDataServiceImpl.java | 41 +- .../ReadWriteNonCoreDataServiceImpl.java | 212 +- .../SchedulerJobRunnerReadServiceImpl.java | 55 +- .../openjpa/OpenJpaTransactionManager.java | 42 + ...portMailingJobReadPlatformServiceImpl.java | 21 +- ...gJobRunHistoryReadPlatformServiceImpl.java | 22 +- .../service/SmsReadPlatformServiceImpl.java | 37 +- .../service/InteropServiceImpl.java | 15 +- .../NotificationReadPlatformServiceImpl.java | 27 +- .../service/TopicDomainServiceImpl.java | 2 + .../OfficeReadPlatformServiceImpl.java | 16 +- ...llerManagementReadPlatformServiceImpl.java | 18 +- ...countTransfersReadPlatformServiceImpl.java | 28 +- ...rtfolioAccountReadPlatformServiceImpl.java | 9 +- ...ructionHistoryReadPlatformServiceImpl.java | 20 +- ...ingInstructionReadPlatformServiceImpl.java | 31 +- ...ngInstructionWritePlatformServiceImpl.java | 9 +- .../client/domain/ClientFamilyMembers.java | 4 +- .../ClientChargeReadPlatformServiceImpl.java | 20 +- .../ClientReadPlatformServiceImpl.java | 25 +- ...entTransactionReadPlatformServiceImpl.java | 24 +- ...ollectionSheetReadPlatformServiceImpl.java | 55 +- .../CenterReadPlatformServiceImpl.java | 26 +- .../service/GroupReadPlatformServiceImpl.java | 14 +- ...erestRateChartReadPlatformServiceImpl.java | 24 +- ...tRateChartSlabReadPlatformServiceImpl.java | 17 +- .../portfolio/loanaccount/domain/Loan.java | 10 +- ...cheduleCalculationPlatformServiceImpl.java | 3 + ...cheduleHistoryReadPlatformServiceImpl.java | 2 + ...heduleHistoryWritePlatformServiceImpl.java | 2 + .../LoanScheduleWritePlatformServiceImpl.java | 2 + .../LoanAccrualWritePlatformServiceImpl.java | 8 +- .../service/LoanArrearsAgingServiceImpl.java | 24 +- .../service/LoanReadPlatformServiceImpl.java | 154 +- .../LoanProductReadPlatformServiceImpl.java | 24 +- ...ostDatedChecksReadPlatformServiceImpl.java | 2 + ...erestRateChartReadPlatformServiceImpl.java | 12 +- ...oldTransactionReadPlatformServiceImpl.java | 23 +- ...DepositAccountReadPlatformServiceImpl.java | 44 +- ...SavingsAccountReadPlatformServiceImpl.java | 61 +- .../SavingsSchedularInterestPoster.java | 4 + .../service/SavingsSchedularServiceImpl.java | 1 + ...ccountDividendReadPlatformServiceImpl.java | 21 +- .../ShareAccountReadPlatformServiceImpl.java | 20 +- ...roductDividendReadPlatformServiceImpl.java | 21 +- .../ShareProductReadPlatformServiceImpl.java | 15 +- .../ScheduledJobRunnerServiceImpl.java | 10 +- ...lidationPolicyReadPlatformServiceImpl.java | 17 +- .../PermissionReadPlatformServiceImpl.java | 21 +- .../db/changelog/db.changelog-master.xml | 1 + .../tenant-store/changelog-tenant-store.xml | 3 +- .../parts/0003_reset_postgresql_sequences.xml | 32 + .../db/changelog/tenant/changelog-tenant.xml | 4 +- .../0003_postgresql_specific_initial_data.xml | 1972 +++++++++++++++++ .../parts/0004_camelcase_column_renaming.xml | 38 + ...WithoutDatabaseAndNoJobsConfiguration.java | 9 + 92 files changed, 3505 insertions(+), 655 deletions(-) create mode 100644 docker-compose-postgresql.yml create mode 100644 fineract-db/docker/postgresql/01-init.sh create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/PostgreSQLQueryService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/openjpa/OpenJpaTransactionManager.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant-store/parts/0003_reset_postgresql_sequences.xml create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0003_postgresql_specific_initial_data.xml create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0004_camelcase_column_renaming.xml diff --git a/docker-compose-postgresql.yml b/docker-compose-postgresql.yml new file mode 100644 index 000000000..1c9a41f56 --- /dev/null +++ b/docker-compose-postgresql.yml @@ -0,0 +1,107 @@ +# 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. +# + +# NB: Travis CI (dist: bionic) supports up to v3.7 +# (unless we'd install a more recent version in .travis.yml) +version: '3.7' +services: + # Backend service + fineractpostgresql: + image: postgres:13.4 + volumes: + - ./fineract-db/docker/postgresql:/docker-entrypoint-initdb.d/:Z,ro + restart: always + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: skdcnwauicn2ucnaecasdsajdnizucawencascdca + FINERACT_DB_USER: postgres + FINERACT_DB_PASS: skdcnwauicn2ucnaecasdsajdnizucawencascdca + FINERACT_TENANTS_DB_NAME: fineract_tenants + FINERACT_TENANT_DEFAULT_DB_NAME: fineract_default + healthcheck: + test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U", "root" ] + timeout: 10s + retries: 10 + ports: + - "5432:5432" + fineract-server: + image: fineract:latest + volumes: + - ./fineract-provider/build/data:/data + healthcheck: + test: ["CMD", 'sh', '-c', 'echo -e "Checking for the availability of Fineract server deployment"; while ! nc -z "fineract-server" 8443; do sleep 1; printf "-"; done; echo -e " >> Fineract server has started";' ] + timeout: 10s + retries: 10 + ports: + - 8443:8443 + depends_on: + fineractpostgresql: + condition: service_healthy + environment: + # TODO: env vars prefixed with "fineract_tenants_*" will be removed with one of the next releases + #- fineract_tenants_driver=org.postgresql.Driver + #- fineract_tenants_url=jdbc:postgresql://fineractpostgresql:5432/fineract_tenants + #- fineract_tenants_uid=postgres + #- fineract_tenants_pwd=skdcnwauicn2ucnaecasdsajdnizucawencascdca + # NOTE: node aware scheduler + - FINERACT_NODE_ID=1 + # NOTE: env vars prefixed "FINERACT_HIKARI_*" are used to configure the database connection pool + - FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME=org.postgresql.Driver + - FINERACT_HIKARI_JDBC_URL=jdbc:postgresql://fineractpostgresql:5432/fineract_tenants + - FINERACT_HIKARI_USERNAME=postgres + - FINERACT_HIKARI_PASSWORD=skdcnwauicn2ucnaecasdsajdnizucawencascdca + # ... following variables are optional; "application.properties" contains reasonable defaults (same as here) + - FINERACT_HIKARI_MINIMUM_IDLE=3 + - FINERACT_HIKARI_MAXIMUM_POOL_SIZE=10 + - FINERACT_HIKARI_IDLE_TIMEOUT=60000 + - FINERACT_HIKARI_CONNECTION_TIMEOUT=20000 + - FINERACT_HIKARI_TEST_QUERY=SELECT 1 + - FINERACT_HIKARI_AUTO_COMMIT=true + - FINERACT_HIKARI_DS_PROPERTIES_CACHE_PREP_STMTS=true + - FINERACT_HIKARI_DS_PROPERTIES_PREP_STMT_CACHE_SIZE=250 + - FINERACT_HIKARI_DS_PROPERTIES_PREP_STMT_CACHE_SQL_LIMIT=2048 + - FINERACT_HIKARI_DS_PROPERTIES_USE_SERVER_PREP_STMTS=true + - FINERACT_HIKARI_DS_PROPERTIES_USE_LOCAL_SESSION_STATE=true + - FINERACT_HIKARI_DS_PROPERTIES_REWRITE_BATCHED_STATEMENTS=true + - FINERACT_HIKARI_DS_PROPERTIES_CACHE_RESULT_SET_METADATA=true + - FINERACT_HIKARI_DS_PROPERTIES_CACHE_SERVER_CONFIGURATION=true + - FINERACT_HIKARI_DS_PROPERTIES_ELIDE_SET_AUTO_COMMITS=true + - FINERACT_HIKARI_DS_PROPERTIES_MAINTAIN_TIME_STATS=false + - FINERACT_HIKARI_DS_PROPERTIES_LOG_SLOW_QUERIES=true + - FINERACT_HIKARI_DS_PROPERTIES_DUMP_QUERIES_IN_EXCEPTION=true + # NOTE: env vars prefixed "FINERACT_DEFAULT_TENANTDB_*" are used to create the default tenant database + - FINERACT_DEFAULT_TENANTDB_HOSTNAME=fineractpostgresql + - FINERACT_DEFAULT_TENANTDB_PORT=5432 + - FINERACT_DEFAULT_TENANTDB_UID=postgres + - FINERACT_DEFAULT_TENANTDB_PWD=skdcnwauicn2ucnaecasdsajdnizucawencascdca + - FINERACT_DEFAULT_TENANTDB_CONN_PARAMS= + - FINERACT_DEFAULT_TENANTDB_TIMEZONE=Asia/Kolkata + - FINERACT_DEFAULT_TENANTDB_IDENTIFIER=default + - FINERACT_DEFAULT_TENANTDB_NAME=fineract_default + - FINERACT_DEFAULT_TENANTDB_DESCRIPTION=Default Demo Tenant + + # Frontend service + community-app: + image: openmf/community-app:latest + container_name: mifos-ui + restart: always + ports: + - 9090:80 + depends_on: + fineract-server: + condition: service_healthy diff --git a/docker-compose.yml b/docker-compose.yml index c38e67017..1a3ba7ca8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,7 +57,7 @@ services: # NOTE: node aware scheduler - FINERACT_NODE_ID=1 # NOTE: env vars prefixed "FINERACT_HIKARI_*" are used to configure the database connection pool - - FINERACT_HIKARI_DRIVER_CLASS_NAME=org.mariadb.jdbc.Driver + - FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME=org.mariadb.jdbc.Driver - FINERACT_HIKARI_JDBC_URL=jdbc:mariadb://fineractmysql:3306/fineract_tenants - FINERACT_HIKARI_USERNAME=root - FINERACT_HIKARI_PASSWORD=skdcnwauicn2ucnaecasdsajdnizucawencascdca diff --git a/fineract-db/docker/postgresql/01-init.sh b/fineract-db/docker/postgresql/01-init.sh new file mode 100644 index 000000000..7a8fd4904 --- /dev/null +++ b/fineract-db/docker/postgresql/01-init.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +# 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. +# + +set -e +export PGPASSWORD=$POSTGRES_PASSWORD; +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE USER $FINERACT_DB_USER WITH PASSWORD '$FINERACT_DB_PASS'; + CREATE DATABASE $FINERACT_TENANTS_DB_NAME; + CREATE DATABASE $FINERACT_TENANT_DEFAULT_DB_NAME; + GRANT ALL PRIVILEGES ON DATABASE $FINERACT_TENANTS_DB_NAME TO $FINERACT_DB_USER; + GRANT ALL PRIVILEGES ON DATABASE $FINERACT_TENANT_DEFAULT_DB_NAME TO $FINERACT_DB_USER; +EOSQL \ No newline at end of file diff --git a/fineract-provider/build.gradle b/fineract-provider/build.gradle index 56bf75b0b..960de2134 100644 --- a/fineract-provider/build.gradle +++ b/fineract-provider/build.gradle @@ -200,6 +200,7 @@ configurations { } dependencies { driver 'org.mariadb.jdbc:mariadb-java-client:2.7.4' + driver 'org.postgresql:postgresql:42.1.4' } URLClassLoader loader = GroovyObject.class.classLoader @@ -237,6 +238,7 @@ bootRun { dependencies { implementation 'org.mariadb.jdbc:mariadb-java-client:2.7.4' + implementation 'org.postgresql:postgresql:42.1.4' } classpath += files("build/generated/swagger-ui") @@ -316,6 +318,7 @@ jib { dependencies { implementation 'org.mariadb.jdbc:mariadb-java-client:2.7.4' + implementation 'org.postgresql:postgresql:42.1.4' } extraDirectories { diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java index 6547e888e..a72e74c8d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryReadPlatformServiceImpl.java @@ -49,6 +49,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.office.data.OfficeData; @@ -77,18 +78,22 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat private final ColumnValidator columnValidator; private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public JournalEntryReadPlatformServiceImpl(final RoutingDataSource dataSource, final GLAccountReadPlatformService glAccountReadPlatformService, final ColumnValidator columnValidator, final OfficeReadPlatformService officeReadPlatformService, - final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper) { + final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.glAccountReadPlatformService = glAccountReadPlatformService; this.officeReadPlatformService = officeReadPlatformService; this.financialActivityAccountRepositoryWrapper = financialActivityAccountRepositoryWrapper; this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } private static final class GLJournalEntryMapper implements RowMapper { @@ -250,7 +255,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData); final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(rm.schema()); final Object[] objectArray = new Object[15]; @@ -304,28 +309,19 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat String fromDateString = null; String toDateString = null; if (fromDate != null && toDate != null) { - sqlBuilder.append(whereClose + " journalEntry.entry_date between ? and ? "); - - whereClose = " and "; - fromDateString = df.format(fromDate); toDateString = df.format(toDate); - objectArray[arrayPos] = fromDateString; - arrayPos = arrayPos + 1; - objectArray[arrayPos] = toDateString; - arrayPos = arrayPos + 1; + sqlBuilder.append(whereClose + " journalEntry.entry_date between '" + fromDateString + "' and '" + toDateString + "' "); + + whereClose = " and "; } else if (fromDate != null) { - sqlBuilder.append(whereClose + " journalEntry.entry_date >= ? "); fromDateString = df.format(fromDate); - objectArray[arrayPos] = fromDateString; - arrayPos = arrayPos + 1; + sqlBuilder.append(whereClose + " journalEntry.entry_date >= '" + fromDateString + "' "); whereClose = " and "; } else if (toDate != null) { - sqlBuilder.append(whereClose + " journalEntry.entry_date <= ? "); toDateString = df.format(toDate); - objectArray[arrayPos] = toDateString; - arrayPos = arrayPos + 1; + sqlBuilder.append(whereClose + " journalEntry.entry_date <= '" + toDateString + "' "); whereClose = " and "; } @@ -368,15 +364,16 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, rm); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, rm); } @Override @@ -530,9 +527,8 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat final GLJournalEntryMapper rm = new GLJournalEntryMapper(associationParametersData); final String sql = "select " + rm.schema() + " where journalEntry.transaction_id = ? and journalEntry.entity_id = ? and journalEntry.entity_type_enum = ?"; - final String sqlCountRows = "SELECT FOUND_ROWS()"; Object[] data = { transactionId, entityId, entityType }; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sql, data, rm); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sql, data, rm); } catch (final EmptyResultDataAccessException e) { throw new JournalEntriesNotFoundException(entityId, e); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryRunningBalanceUpdateServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryRunningBalanceUpdateServiceImpl.java index af3426666..99904ffc7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryRunningBalanceUpdateServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryRunningBalanceUpdateServiceImpl.java @@ -39,6 +39,7 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; @@ -62,41 +63,19 @@ public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntry private final JournalEntryDataValidator dataValidator; private final FromJsonHelper fromApiJsonHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final GLJournalEntryMapper entryMapper = new GLJournalEntryMapper(); - // if a limit is not added to the running balance select statements below - // and the resultset is more than 400,000, - // the script will eat up all of the server memory - private final String selectRunningBalanceSqlLimit = "limit 0, 10000"; - - private final String officeRunningBalanceSql = "select je.office_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je " - + "inner join (select max(id) as id from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id,entry_date) je2 " - + "inner join (select max(entry_date) as date from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id) je3 " - + "where je2.id = je.id and je.entry_date = je3.date group by je.id order by je.entry_date DESC " - + selectRunningBalanceSqlLimit; - - private final String organizationRunningBalanceSql = "select je.organization_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je " - + "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by account_id,entry_date) je2 " - + "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by account_id) je3 " - + "where je2.id = je.id and je.entry_date = je3.date group by je.id order by je.entry_date DESC " - + selectRunningBalanceSqlLimit; - - private final String officesRunningBalanceSql = "select je.office_running_balance as runningBalance,je.account_id as accountId,je.office_id as officeId " - + "from acc_gl_journal_entry je " - + "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by office_id,account_id,entry_date) je2 " - + "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by office_id,account_id) je3 " - + "where je2.id = je.id and je.entry_date = je3.date group by je.id order by je.entry_date DESC " - + selectRunningBalanceSqlLimit; - @Autowired public JournalEntryRunningBalanceUpdateServiceImpl(final RoutingDataSource dataSource, final OfficeRepositoryWrapper officeRepositoryWrapper, final JournalEntryDataValidator dataValidator, - final FromJsonHelper fromApiJsonHelper) { + final FromJsonHelper fromApiJsonHelper, DatabaseSpecificSQLGenerator sqlGenerator) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.officeRepositoryWrapper = officeRepositoryWrapper; this.dataValidator = dataValidator; this.fromApiJsonHelper = fromApiJsonHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -140,7 +119,12 @@ public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntry Map runningBalanceMap = new HashMap<>(5); Map> officesRunningBalance = new HashMap<>(); - List> list = jdbcTemplate.queryForList(organizationRunningBalanceSql, entityDate, entityDate); + List> list = jdbcTemplate.queryForList( + "select je.organization_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je " + + "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by account_id,entry_date) je2 ON je2.id = je.id " + + "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by account_id) je3 ON je.entry_date = je3.date " + + "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0), + entityDate, entityDate); for (Map entries : list) { Long accountId = Long.parseLong(entries.get("accountId").toString()); // Drizzle // is @@ -157,7 +141,12 @@ public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntry } } - List> officesRunningBalanceList = jdbcTemplate.queryForList(officesRunningBalanceSql, entityDate, entityDate); + List> officesRunningBalanceList = jdbcTemplate + .queryForList("select je.office_running_balance as runningBalance,je.account_id as accountId,je.office_id as officeId " + + "from acc_gl_journal_entry je " + + "inner join (select max(id) as id from acc_gl_journal_entry where entry_date < ? group by office_id,account_id,entry_date) je2 ON je2.id = je.id " + + "inner join (select max(entry_date) as date from acc_gl_journal_entry where entry_date < ? group by office_id,account_id) je3 ON je.entry_date = je3.date " + + "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0), entityDate, entityDate); for (Map entries : officesRunningBalanceList) { Long accountId = Long.parseLong(entries.get("accountId").toString()); Long officeId = Long.parseLong(entries.get("officeId").toString()); @@ -212,7 +201,12 @@ public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntry private void updateRunningBalance(Long officeId, Date entityDate) { Map runningBalanceMap = new HashMap<>(5); - List> list = jdbcTemplate.queryForList(officeRunningBalanceSql, officeId, entityDate, officeId, entityDate); + List> list = jdbcTemplate.queryForList( + "select je.office_running_balance as runningBalance,je.account_id as accountId from acc_gl_journal_entry je " + + "inner join (select max(id) as id from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id,entry_date) je2 ON je2.id = je.id " + + "inner join (select max(entry_date) as date from acc_gl_journal_entry where office_id=? and entry_date < ? group by account_id) je3 ON je.entry_date = je3.date " + + "group by je.id order by je.entry_date DESC " + sqlGenerator.limit(10000, 0), + officeId, entityDate, officeId, entityDate); for (Map entries : list) { Long accountId = (Long) entries.get("accountId"); if (!runningBalanceMap.containsKey(accountId)) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java index a2ac521fa..8705fd55f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java @@ -32,6 +32,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -46,18 +47,23 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE private static final Logger LOG = LoggerFactory.getLogger(ProvisioningEntriesReadPlatformServiceImpl.class); private final JdbcTemplate jdbcTemplate; - private final PaginationHelper loanProductProvisioningEntryDataPaginationHelper = new PaginationHelper<>(); - private final PaginationHelper provisioningEntryDataPaginationHelper = new PaginationHelper<>(); + private final PaginationHelper loanProductProvisioningEntryDataPaginationHelper; + private final PaginationHelper provisioningEntryDataPaginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired - public ProvisioningEntriesReadPlatformServiceImpl(final RoutingDataSource dataSource) { + public ProvisioningEntriesReadPlatformServiceImpl(final RoutingDataSource dataSource, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.loanProductProvisioningEntryDataPaginationHelper = paginationHelper; + this.provisioningEntryDataPaginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override public Collection retrieveLoanProductsProvisioningData(Date date) { String formattedDate = new SimpleDateFormat("yyyy-MM-dd").format(date); - LoanProductProvisioningEntryMapper mapper = new LoanProductProvisioningEntryMapper(); + LoanProductProvisioningEntryMapper mapper = new LoanProductProvisioningEntryMapper(sqlGenerator); final String sql = mapper.schema(); return this.jdbcTemplate.query(sql, mapper, new Object[] { formattedDate, formattedDate, formattedDate }); } @@ -66,17 +72,17 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE private final StringBuilder sqlQuery; - private LoanProductProvisioningEntryMapper() { + private LoanProductProvisioningEntryMapper(DatabaseSpecificSQLGenerator sqlGenerator) { sqlQuery = new StringBuilder().append( "select (CASE WHEN loan.loan_type_enum=1 THEN mclient.office_id ELSE mgroup.office_id END) as office_id, loan.loan_type_enum, pcd.criteria_id as criteriaid, loan.product_id,loan.currency_code,") - .append("GREATEST(datediff(?") - .append(",sch.duedate),0) as numberofdaysoverdue,sch.duedate, pcd.category_id, pcd.provision_percentage,") + .append("GREATEST(" + sqlGenerator.dateDiff("?", "sch.duedate") + + ", 0) as numberofdaysoverdue,sch.duedate, pcd.category_id, pcd.provision_percentage,") .append("loan.total_outstanding_derived as outstandingbalance, pcd.liability_account, pcd.expense_account from m_loan_repayment_schedule sch") .append(" LEFT JOIN m_loan loan on sch.loan_id = loan.id") .append(" JOIN m_loanproduct_provisioning_mapping lpm on lpm.product_id = loan.product_id") .append(" JOIN m_provisioning_criteria_definition pcd on pcd.criteria_id = lpm.criteria_id and ") - .append("(pcd.min_age <= GREATEST(datediff(?,sch.duedate),0) and GREATEST(datediff(?") - .append(",sch.duedate),0) <= pcd.max_age) and pcd.criteria_id is not null ") + .append("(pcd.min_age <= GREATEST(" + sqlGenerator.dateDiff("?", "sch.duedate") + ",0) and GREATEST(" + + sqlGenerator.dateDiff("?", "sch.duedate") + ",0) <= pcd.max_age) and pcd.criteria_id is not null ") .append("LEFT JOIN m_client mclient ON mclient.id = loan.client_id ") .append("LEFT JOIN m_group mgroup ON mgroup.id = loan.group_id ") .append("where loan.loan_status_id=300 and sch.duedate = ") @@ -221,7 +227,7 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE public Page retrieveAllProvisioningEntries(Integer offset, Integer limit) { ProvisioningEntryDataMapper mapper = new ProvisioningEntryDataMapper(); StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(mapper.getSchema()); sqlBuilder.append(" order by entry.created_date"); if (limit != null) { @@ -231,10 +237,9 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE sqlBuilder.append(" offset ").append(offset); } - final String sqlCountRows = "SELECT FOUND_ROWS()"; Object[] whereClauseItemsitems = new Object[] {}; - return this.provisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), - whereClauseItemsitems, mapper); + return this.provisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), whereClauseItemsitems, + mapper); } @Override @@ -308,7 +313,7 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE public Page retrieveProvisioningEntries(SearchParameters searchParams) { LoanProductProvisioningEntryRowMapper mapper = new LoanProductProvisioningEntryRowMapper(); final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(mapper.getSchema()); String whereClose = " where "; List items = new ArrayList<>(); @@ -344,8 +349,7 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE } } Object[] whereClauseItemsitems = items.toArray(); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.loanProductProvisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), + return this.loanProductProvisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), whereClauseItemsitems, mapper); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java index 2f7782a5c..9f36da6fb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/domain/AdHoc.java @@ -55,7 +55,7 @@ public final class AdHoc extends AbstractAuditableCustom { @Column(name = "report_run_every") private Long reportRunEvery; - @Column(name = "IsActive", nullable = false) + @Column(name = "is_active", nullable = false) private boolean isActive = false; private AdHoc() {} diff --git a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java index 56bf6c1be..99bc58c75 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/adhocquery/service/AdHocReadPlatformServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.fineract.adhocquery.data.AdHocData; import org.apache.fineract.adhocquery.exception.AdHocNotFoundException; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; @@ -36,12 +37,14 @@ import org.springframework.stereotype.Service; public class AdHocReadPlatformServiceImpl implements AdHocReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final AdHocMapper adHocRowMapper; @Autowired - public AdHocReadPlatformServiceImpl(final RoutingDataSource dataSource) { + public AdHocReadPlatformServiceImpl(final RoutingDataSource dataSource, DatabaseSpecificSQLGenerator sqlGenerator) { this.jdbcTemplate = new JdbcTemplate(dataSource); - this.adHocRowMapper = new AdHocMapper(); + this.sqlGenerator = sqlGenerator; + this.adHocRowMapper = new AdHocMapper(sqlGenerator); } @Override @@ -53,7 +56,8 @@ public class AdHocReadPlatformServiceImpl implements AdHocReadPlatformService { @Override public Collection retrieveAllActiveAdHocQuery() { - final String sql = "select " + this.adHocRowMapper.schema() + " where r.IsActive = true order by r.id"; + final String sql = "select " + this.adHocRowMapper.schema() + " where r." + sqlGenerator.escape("is_active") + + " = true order by r.id"; return this.jdbcTemplate.query(sql, this.adHocRowMapper); } @@ -72,6 +76,12 @@ public class AdHocReadPlatformServiceImpl implements AdHocReadPlatformService { protected static final class AdHocMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + public AdHocMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + @Override public AdHocData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { @@ -96,7 +106,7 @@ public class AdHocReadPlatformServiceImpl implements AdHocReadPlatformService { } public String schema() { - return " r.id as id, r.name as name, r.query as query, r.table_name as tableName,r.table_fields as tableField ,r.IsActive as isActive ,r.email as email ," + return " r.id as id, r.name as name, r.query as query, r.table_name as tableName,r.table_fields as tableField ,r.is_active as isActive ,r.email as email ," + " r.report_run_frequency_code, r.report_run_every, r.last_run, " + " r.created_date as createdDate, r.createdby_id as createdById,cb.username as createdBy,r.lastmodifiedby_id as updatedById ,r.lastmodified_date as updatedOn " + " from m_adhoc r left join m_appuser cb on cb.id=r.createdby_id left join m_appuser mb on mb.id=r.lastmodifiedby_id"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/AuditReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/AuditReadPlatformServiceImpl.java index dbf9d7be0..a0b83652c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/AuditReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/AuditReadPlatformServiceImpl.java @@ -43,6 +43,7 @@ import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.SQLBuilder; @@ -87,7 +88,8 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService { private final ClientReadPlatformService clientReadPlatformService; private final LoanProductReadPlatformService loanProductReadPlatformService; private final StaffReadPlatformService staffReadPlatformService; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PaginationParametersDataValidator paginationParametersDataValidator; private final SavingsProductReadPlatformService savingsProductReadPlatformService; private final DepositProductReadPlatformService depositProductReadPlatformService; @@ -100,7 +102,8 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService { final LoanProductReadPlatformService loanProductReadPlatformService, final StaffReadPlatformService staffReadPlatformService, final PaginationParametersDataValidator paginationParametersDataValidator, final SavingsProductReadPlatformService savingsProductReadPlatformService, - final DepositProductReadPlatformService depositProductReadPlatformService, final ColumnValidator columnValidator) { + final DepositProductReadPlatformService depositProductReadPlatformService, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.fromApiJsonHelper = fromApiJsonHelper; @@ -113,6 +116,8 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService { this.savingsProductReadPlatformService = savingsProductReadPlatformService; this.depositProductReadPlatformService = depositProductReadPlatformService; this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } private static final class AuditMapper implements RowMapper { @@ -199,7 +204,7 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService { final AuditMapper rm = new AuditMapper(); final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(rm.schema(includeJson, hierarchy)); sqlBuilder.append(' ').append(extraCriteria.getSQLTemplate()); if (parameters.isOrderByRequested()) { @@ -216,8 +221,7 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService { LOG.info("sql: {}", sqlBuilder); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), extraCriteria.getArguments(), rm); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), extraCriteria.getArguments(), rm); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java index 72ac2264b..5ac15dec2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java @@ -60,7 +60,7 @@ public class EmailCampaign extends AbstractPersistableCustom { private Integer campaignType; @ManyToOne - @JoinColumn(name = "businessRule_id", nullable = false) + @JoinColumn(name = "business_rule_id", nullable = false) private Report businessRuleId; @Column(name = "param_value") diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformServiceImpl.java index 4d92ea25b..c7ca45de2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformServiceImpl.java @@ -72,7 +72,7 @@ public class EmailCampaignReadPlatformServiceImpl implements EmailCampaignReadPl sql.append("ec.id as id, "); sql.append("ec.campaign_name as campaignName, "); sql.append("ec.campaign_type as campaignType, "); - sql.append("ec.businessRule_id as businessRuleId, "); + sql.append("ec.business_rule_id as businessRuleId, "); sql.append("ec.email_subject as emailSubject, "); sql.append("ec.email_message as emailMessage, "); sql.append("ec.email_attachment_file_format as emailAttachmentFileFormat, "); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java index 71b833284..f16cff55f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java @@ -36,6 +36,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; @@ -46,12 +47,16 @@ import org.springframework.stereotype.Service; public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final EmailMapper emailRowMapper = new EmailMapper(); - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; @Autowired - public EmailReadPlatformServiceImpl(final RoutingDataSource dataSource) { + public EmailReadPlatformServiceImpl(final RoutingDataSource dataSource, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; + this.paginationHelper = paginationHelper; } private static final class EmailMapper implements RowMapper { @@ -129,7 +134,7 @@ public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { @Override public Collection retrieveAllPending(final SearchParameters searchParameters) { - final String sqlPlusLimit = (searchParameters.getLimit() > 0) ? " limit 0, " + searchParameters.getLimit() : ""; + final String sqlPlusLimit = (searchParameters.getLimit() > 0) ? " " + sqlGenerator.limit(searchParameters.getLimit()) : ""; final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum =? " + sqlPlusLimit; return this.jdbcTemplate.query(sql, this.emailRowMapper, EmailMessageStatusType.PENDING.getValue()); @@ -137,7 +142,7 @@ public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { @Override public Collection retrieveAllSent(final SearchParameters searchParameters) { - final String sqlPlusLimit = (searchParameters.getLimit() > 0) ? " limit 0, " + searchParameters.getLimit() : ""; + final String sqlPlusLimit = (searchParameters.getLimit() > 0) ? " " + sqlGenerator.limit(searchParameters.getLimit()) : ""; final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = ?" + sqlPlusLimit; return this.jdbcTemplate.query(sql, this.emailRowMapper, EmailMessageStatusType.SENT.getValue()); @@ -145,7 +150,7 @@ public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { @Override public List retrieveExternalIdsOfAllSent(final Integer limit) { - final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : ""; + final String sqlPlusLimit = (limit > 0) ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select external_id from " + this.emailRowMapper.tableName() + " where status_enum =? " + sqlPlusLimit; return this.jdbcTemplate.queryForList(sql, Long.class, EmailMessageStatusType.SENT.getValue()); @@ -153,7 +158,7 @@ public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { @Override public Collection retrieveAllDelivered(final Integer limit) { - final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : ""; + final String sqlPlusLimit = (limit > 0) ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = ?" + sqlPlusLimit; return this.jdbcTemplate.query(sql, this.emailRowMapper, EmailMessageStatusType.DELIVERED.getValue()); @@ -161,7 +166,7 @@ public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { @Override public Collection retrieveAllFailed(final SearchParameters searchParameters) { - final String sqlPlusLimit = (searchParameters.getLimit() > 0) ? " limit 0, " + searchParameters.getLimit() : ""; + final String sqlPlusLimit = (searchParameters.getLimit() > 0) ? " " + sqlGenerator.limit(searchParameters.getLimit()) : ""; final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = ?" + sqlPlusLimit; return this.jdbcTemplate.query(sql, this.emailRowMapper, EmailMessageStatusType.FAILED.getValue()); @@ -170,7 +175,7 @@ public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { @Override public Page retrieveEmailByStatus(final Integer limit, final Integer status, final Date dateFrom, final Date dateTo) { final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.emailRowMapper.schema()); if (status != null) { sqlBuilder.append(" where emo.status_enum= ? "); @@ -183,12 +188,11 @@ public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { toDateString = df.format(dateTo); sqlBuilder.append(" and emo.submittedon_date >= ? and emo.submittedon_date <= ? "); } - final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : ""; + final String sqlPlusLimit = (limit > 0) ? " " + sqlGenerator.limit(limit) : ""; if (!sqlPlusLimit.isEmpty()) { sqlBuilder.append(sqlPlusLimit); } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), new Object[] { status, fromDateString, toDateString }, this.emailRowMapper); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java index 89d77511f..36824faac 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java @@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.portfolio.calendar.service.CalendarDropdownReadPlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; @@ -56,20 +57,24 @@ public class SmsCampaignReadPlatformServiceImpl implements SmsCampaignReadPlatfo private final BusinessRuleMapper businessRuleMapper; private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final SmsCampaignDropdownReadPlatformService smsCampaignDropdownReadPlatformService; private final SmsCampaignMapper smsCampaignMapper; private final CalendarDropdownReadPlatformService calendarDropdownReadPlatformService; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; @Autowired public SmsCampaignReadPlatformServiceImpl(final RoutingDataSource dataSource, SmsCampaignDropdownReadPlatformService smsCampaignDropdownReadPlatformService, - final CalendarDropdownReadPlatformService calendarDropdownReadPlatformService) { + final CalendarDropdownReadPlatformService calendarDropdownReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); - businessRuleMapper = new BusinessRuleMapper(); + this.sqlGenerator = sqlGenerator; + this.businessRuleMapper = new BusinessRuleMapper(sqlGenerator); this.smsCampaignDropdownReadPlatformService = smsCampaignDropdownReadPlatformService; - smsCampaignMapper = new SmsCampaignMapper(); + this.smsCampaignMapper = new SmsCampaignMapper(); this.calendarDropdownReadPlatformService = calendarDropdownReadPlatformService; + this.paginationHelper = paginationHelper; } @Override @@ -87,17 +92,18 @@ public class SmsCampaignReadPlatformServiceImpl implements SmsCampaignReadPlatfo public Page retrieveAll(final SearchParameters searchParameters) { final Integer visible = 1; final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.smsCampaignMapper.schema() + " where sc.is_visible = ? "); + if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(jdbcTemplate, sqlCountRows, sqlBuilder.toString(), new Object[] { visible }, - this.smsCampaignMapper); + return this.paginationHelper.fetchPage(jdbcTemplate, sqlBuilder.toString(), new Object[] { visible }, this.smsCampaignMapper); } @Override @@ -133,7 +139,7 @@ public class SmsCampaignReadPlatformServiceImpl implements SmsCampaignReadPlatfo final String schema; - private BusinessRuleMapper() { + private BusinessRuleMapper(DatabaseSpecificSQLGenerator sqlGenerator) { final StringBuilder sql = new StringBuilder(300); sql.append("sr.id as id, "); sql.append("sr.report_name as reportName, "); @@ -141,7 +147,7 @@ public class SmsCampaignReadPlatformServiceImpl implements SmsCampaignReadPlatfo sql.append("sr.report_subtype as reportSubType, "); sql.append("sr.description as description, "); sql.append("sp.parameter_variable as params, "); - sql.append("sp.parameter_FormatType as paramType, "); + sql.append("sp." + sqlGenerator.escape("parameter_FormatType") + " as paramType, "); sql.append("sp.parameter_label as paramLabel, "); sql.append("sp.parameter_name as paramName "); sql.append("from stretchy_report sr "); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/PersistenceConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/PersistenceConfig.java index 4b1481207..071cbe520 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/PersistenceConfig.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/PersistenceConfig.java @@ -22,6 +22,7 @@ package org.apache.fineract.infrastructure.core.config; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.apache.fineract.infrastructure.core.domain.AuditorAwareImpl; +import org.apache.fineract.infrastructure.openjpa.OpenJpaTransactionManager; import org.apache.fineract.infrastructure.openjpa.OpenJpaVendorAdapter; import org.apache.fineract.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; @@ -58,8 +59,8 @@ public class PersistenceConfig { } @Bean - public JpaTransactionManager transactionManager(EntityManagerFactory emf) { - JpaTransactionManager jtm = new JpaTransactionManager(); + public OpenJpaTransactionManager transactionManager(EntityManagerFactory emf) { + OpenJpaTransactionManager jtm = new OpenJpaTransactionManager(); jtm.setEntityManagerFactory(emf); return jtm; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/PaginationHelper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/PaginationHelper.java index ddaaadf34..a91c5ecd3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/PaginationHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/PaginationHelper.java @@ -20,26 +20,46 @@ package org.apache.fineract.infrastructure.core.service; import java.util.List; import org.apache.commons.lang3.ObjectUtils; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; -public class PaginationHelper { +@Component +public class PaginationHelper { - public Page fetchPage(final JdbcTemplate jt, final String sqlCountRows, final String sqlFetchRows, final Object[] args, - final RowMapper rowMapper) { + private final DatabaseSpecificSQLGenerator sqlGenerator; + private final DatabaseTypeResolver databaseTypeResolver; + + @Autowired + public PaginationHelper(DatabaseSpecificSQLGenerator sqlGenerator, DatabaseTypeResolver databaseTypeResolver) { + this.sqlGenerator = sqlGenerator; + this.databaseTypeResolver = databaseTypeResolver; + } + + public Page fetchPage(final JdbcTemplate jt, final String sqlFetchRows, final Object[] args, final RowMapper rowMapper) { final List items = jt.query(sqlFetchRows, rowMapper, args); // determine how many rows are available - final int totalFilteredRecords = jt.queryForObject(sqlCountRows, Integer.class); + String sqlCountRows = sqlGenerator.countLastExecutedQueryResult(sqlFetchRows); + final int totalFilteredRecords; + if (databaseTypeResolver.isMySQL()) { + totalFilteredRecords = jt.queryForObject(sqlCountRows, Integer.class); + } else { + totalFilteredRecords = jt.queryForObject(sqlCountRows, Integer.class, args); + } return new Page<>(items, totalFilteredRecords); } - public Page fetchPage(JdbcTemplate jdbcTemplate, String sql, String sqlCountRows, Class type) { + public Page fetchPage(JdbcTemplate jdbcTemplate, String sql, Class type) { final List items = jdbcTemplate.queryForList(sql, type); // determine how many rows are available + String sqlCountRows = sqlGenerator.countLastExecutedQueryResult(sql); Integer totalFilteredRecords = jdbcTemplate.queryForObject(sqlCountRows, Integer.class); return new Page<>(items, ObjectUtils.defaultIfNull(totalFilteredRecords, 0)); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseIndependentQueryService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseIndependentQueryService.java index d472521c7..b62fba71e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseIndependentQueryService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseIndependentQueryService.java @@ -24,6 +24,7 @@ import java.sql.SQLException; import java.util.Collection; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Component; @Component @@ -40,7 +41,7 @@ public class DatabaseIndependentQueryService implements DatabaseQueryService { try { DatabaseQueryService result = null; if (isNotEmpty(queryServices)) { - result = queryServices.stream().filter(qs -> qs.isSupported(dataSource)).findAny().orElse(null); + result = queryServices.stream().filter(DatabaseQueryService::isSupported).findAny().orElse(null); } if (result == null) { throw new IllegalStateException("DataSource not supported: " + dataSource.getConnection().getMetaData().getURL()); @@ -52,7 +53,7 @@ public class DatabaseIndependentQueryService implements DatabaseQueryService { } @Override - public boolean isSupported(DataSource dataSource) { + public boolean isSupported() { return true; } @@ -60,4 +61,9 @@ public class DatabaseIndependentQueryService implements DatabaseQueryService { public boolean isTablePresent(DataSource dataSource, String tableName) { return choose(dataSource).isTablePresent(dataSource, tableName); } + + @Override + public SqlRowSet getTableColumns(DataSource dataSource, String tableName) { + return choose(dataSource).getTableColumns(dataSource, tableName); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseQueryService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseQueryService.java index 38fc65f85..b004685e4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseQueryService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseQueryService.java @@ -19,10 +19,14 @@ package org.apache.fineract.infrastructure.core.service.database; import javax.sql.DataSource; +import org.springframework.jdbc.support.rowset.SqlRowSet; public interface DatabaseQueryService { - boolean isSupported(DataSource dataSource); + boolean isSupported(); boolean isTablePresent(DataSource dataSource, String tableName); + + // TODO: This needs to be improved to have a custom POJO return type instead of the raw SqlRowSet + SqlRowSet getTableColumns(DataSource dataSource, String tableName); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java new file mode 100644 index 000000000..9c3a20f84 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseSpecificSQLGenerator.java @@ -0,0 +1,150 @@ +/** + * 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.infrastructure.core.service.database; + +import static java.lang.String.format; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class DatabaseSpecificSQLGenerator { + + private final DatabaseTypeResolver databaseTypeResolver; + + @Autowired + public DatabaseSpecificSQLGenerator(DatabaseTypeResolver databaseTypeResolver) { + this.databaseTypeResolver = databaseTypeResolver; + } + + public String escape(String arg) { + if (databaseTypeResolver.isMySQL()) { + return format("`%s`", arg); + } else if (databaseTypeResolver.isPostgreSQL()) { + return format("\"%s\"", arg); + } + return arg; + } + + public String groupConcat(String arg) { + if (databaseTypeResolver.isMySQL()) { + return format("GROUP_CONCAT(%s)", arg); + } else if (databaseTypeResolver.isPostgreSQL()) { + // STRING_AGG only works with strings + return format("STRING_AGG(%s::varchar, ',')", arg); + } else { + throw new IllegalStateException("Database type is not supported for group concat " + databaseTypeResolver.databaseType()); + } + } + + public String limit(int count) { + return limit(count, 0); + } + + public String limit(int count, int offset) { + if (databaseTypeResolver.isMySQL()) { + return format("LIMIT %s,%s", offset, count); + } else if (databaseTypeResolver.isPostgreSQL()) { + return format("LIMIT %s OFFSET %s", count, offset); + } else { + throw new IllegalStateException("Database type is not supported for limit " + databaseTypeResolver.databaseType()); + } + } + + public String calcFoundRows() { + if (databaseTypeResolver.isMySQL()) { + return "SQL_CALC_FOUND_ROWS"; + } else { + return ""; + } + } + + public String countLastExecutedQueryResult(String sql) { + if (databaseTypeResolver.isMySQL()) { + return "SELECT FOUND_ROWS()"; + } else { + return countQueryResult(sql); + } + } + + public String countQueryResult(String sql) { + return format("SELECT COUNT(*) FROM (%s) AS temp", sql); + } + + public String currentDate() { + if (databaseTypeResolver.isMySQL()) { + return "CURDATE()"; + } else if (databaseTypeResolver.isPostgreSQL()) { + return "CURRENT_DATE"; + } else { + throw new IllegalStateException("Database type is not supported for current date " + databaseTypeResolver.databaseType()); + } + } + + public String subDate(String date, String multiplier, String unit) { + if (databaseTypeResolver.isMySQL()) { + return format("DATE_SUB(%s, INTERVAL %s %s)", date, multiplier, unit); + } else if (databaseTypeResolver.isPostgreSQL()) { + return format("(%s::TIMESTAMP - %s * INTERVAL '1 %s')", date, multiplier, unit); + } else { + throw new IllegalStateException("Database type is not supported for subtracting date " + databaseTypeResolver.databaseType()); + } + } + + public String dateDiff(String date1, String date2) { + if (databaseTypeResolver.isMySQL()) { + return format("DATEDIFF(%s, %s)", date1, date2); + } else if (databaseTypeResolver.isPostgreSQL()) { + return format("EXTRACT(DAY FROM (%s::TIMESTAMP - %s::TIMESTAMP))", date1, date2); + } else { + throw new IllegalStateException("Database type is not supported for date diff " + databaseTypeResolver.databaseType()); + } + } + + public String lastInsertId() { + if (databaseTypeResolver.isMySQL()) { + return "LAST_INSERT_ID()"; + } else if (databaseTypeResolver.isPostgreSQL()) { + return "LASTVAL()"; + } else { + throw new IllegalStateException("Database type is not supported for last insert id " + databaseTypeResolver.databaseType()); + } + } + + public String castChar(String sql) { + if (databaseTypeResolver.isMySQL()) { + return format("CAST(%s AS CHAR)", sql); + } else if (databaseTypeResolver.isPostgreSQL()) { + return format("%s::CHAR", sql); + } else { + throw new IllegalStateException( + "Database type is not supported for casting to character " + databaseTypeResolver.databaseType()); + } + } + + public String currentSchema() { + if (databaseTypeResolver.isMySQL()) { + return "SCHEMA()"; + } else if (databaseTypeResolver.isPostgreSQL()) { + return "CURRENT_SCHEMA()"; + } else { + throw new IllegalStateException("Database type is not supported for current schema " + databaseTypeResolver.databaseType()); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseType.java index feaea7d28..6b3954d33 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseType.java @@ -19,5 +19,5 @@ package org.apache.fineract.infrastructure.core.service.database; public enum DatabaseType { - MYSQL + MYSQL, POSTGRESQL } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseTypeResolver.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseTypeResolver.java index bcfab30b5..b21bfb5ea 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseTypeResolver.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DatabaseTypeResolver.java @@ -18,53 +18,49 @@ */ package org.apache.fineract.infrastructure.core.service.database; -import static org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection.toProtocol; - +import com.zaxxer.hikari.HikariConfig; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import javax.sql.DataSource; -import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import java.util.concurrent.atomic.AtomicReference; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component -public class DatabaseTypeResolver { +public class DatabaseTypeResolver implements InitializingBean { - private final Map protocolMapping = Map.of("jdbc:mariadb", DatabaseType.MYSQL, "jdbc:mysql", DatabaseType.MYSQL); + private final Map driverMapping = Map.of("org.mariadb.jdbc.Driver", DatabaseType.MYSQL, "com.mysql.jdbc.Driver", + DatabaseType.MYSQL, "org.postgresql.Driver", DatabaseType.POSTGRESQL); - private final Map dataSourceCache = new ConcurrentHashMap<>(); - - private final RoutingDataSource routingDataSource; + private final AtomicReference currentDatabaseType = new AtomicReference<>(); + private final HikariConfig hikariConfig; @Autowired - public DatabaseTypeResolver(RoutingDataSource routingDataSource) { - this.routingDataSource = routingDataSource; + public DatabaseTypeResolver(HikariConfig hikariConfig) { + this.hikariConfig = hikariConfig; } - public boolean isMySQL() { - return isMySQL(routingDataSource); + @Override + public void afterPropertiesSet() throws Exception { + currentDatabaseType.set(determineDatabaseType(hikariConfig.getDriverClassName())); } - public boolean isMySQL(DataSource dataSource) { - return DatabaseType.MYSQL.equals(databaseType(dataSource)); + private DatabaseType determineDatabaseType(String driverClassName) { + DatabaseType databaseType = driverMapping.get(driverClassName); + if (databaseType == null) { + throw new IllegalArgumentException("The driver's class is not supported " + driverClassName); + } + return databaseType; } public DatabaseType databaseType() { - return databaseType(routingDataSource); + return currentDatabaseType.get(); } - public DatabaseType databaseType(DataSource dataSource) { - DataSource unwrappedDataSource = dataSource; - if (dataSource instanceof RoutingDataSource) { - unwrappedDataSource = ((RoutingDataSource) dataSource).determineTargetDataSource(); - } - return dataSourceCache.computeIfAbsent(unwrappedDataSource, (ds) -> { - String protocol = toProtocol(ds); - DatabaseType databaseType = protocolMapping.get(protocol); - if (databaseType == null) { - throw new IllegalArgumentException("The DataSource is not supported"); - } - return databaseType; - }); + public boolean isPostgreSQL() { + return DatabaseType.POSTGRESQL.equals(currentDatabaseType.get()); + } + + public boolean isMySQL() { + return DatabaseType.MYSQL.equals(currentDatabaseType.get()); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/MySQLQueryService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/MySQLQueryService.java index 4896a5b5c..2e1493b5e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/MySQLQueryService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/MySQLQueryService.java @@ -19,10 +19,9 @@ package org.apache.fineract.infrastructure.core.service.database; import static java.lang.String.format; -import static org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection.toProtocol; -import java.util.Set; import javax.sql.DataSource; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Component; @@ -30,12 +29,16 @@ import org.springframework.stereotype.Component; @Component public class MySQLQueryService implements DatabaseQueryService { - private final Set supportedProtocols = Set.of("jdbc:mariadb", "jdbc:mysql"); + private final DatabaseTypeResolver databaseTypeResolver; + + @Autowired + public MySQLQueryService(DatabaseTypeResolver databaseTypeResolver) { + this.databaseTypeResolver = databaseTypeResolver; + } @Override - public boolean isSupported(DataSource dataSource) { - String protocol = toProtocol(dataSource); - return supportedProtocols.contains(protocol.toLowerCase()); + public boolean isSupported() { + return databaseTypeResolver.isMySQL(); } @Override @@ -44,4 +47,17 @@ public class MySQLQueryService implements DatabaseQueryService { SqlRowSet rs = jdbcTemplate.queryForRowSet(format("SHOW TABLES LIKE '%s'", tableName)); return rs.next(); } + + @Override + public SqlRowSet getTableColumns(DataSource dataSource, String tableName) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + String sql = "SELECT c.COLUMN_NAME, c.IS_NULLABLE, c.DATA_TYPE, c.CHARACTER_MAXIMUM_LENGTH, c.COLUMN_KEY FROM INFORMATION_SCHEMA.COLUMNS c WHERE TABLE_SCHEMA = schema() AND TABLE_NAME = '" + + tableName + "' ORDER BY ORDINAL_POSITION"; + final SqlRowSet columnDefinitions = jdbcTemplate.queryForRowSet(sql); + if (columnDefinitions.next()) { + return columnDefinitions; + } else { + throw new IllegalArgumentException("Table " + tableName + " is not found"); + } + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/PostgreSQLQueryService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/PostgreSQLQueryService.java new file mode 100644 index 000000000..428b822e4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/PostgreSQLQueryService.java @@ -0,0 +1,65 @@ +/** + * 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.infrastructure.core.service.database; + +import static java.lang.String.format; + +import java.util.Objects; +import javax.sql.DataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.rowset.SqlRowSet; +import org.springframework.stereotype.Component; + +@Component +public class PostgreSQLQueryService implements DatabaseQueryService { + + private final DatabaseTypeResolver databaseTypeResolver; + + @Autowired + public PostgreSQLQueryService(DatabaseTypeResolver databaseTypeResolver) { + this.databaseTypeResolver = databaseTypeResolver; + } + + @Override + public boolean isSupported() { + return databaseTypeResolver.isPostgreSQL(); + } + + @Override + public boolean isTablePresent(DataSource dataSource, String tableName) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + Integer result = jdbcTemplate.queryForObject(format("SELECT COUNT(table_name) " + "FROM information_schema.tables " + + "WHERE table_schema = 'public' " + "AND table_name = '%s';", tableName), Integer.class); + return Objects.equals(result, 1); + } + + @Override + public SqlRowSet getTableColumns(DataSource dataSource, String tableName) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + String sql = "SELECT attname AS COLUMN_NAME, not attnotnull AS IS_NULLABLE, atttypid::regtype AS DATATYPE, attlen AS CHARACTER_MAXIMUM_LENGTH, attnum = 1 AS COLUMN_KEY FROM pg_attribute WHERE attrelid = '\"" + + tableName + "\"'::regclass AND attnum > 0 AND NOT attisdropped ORDER BY attnum"; + final SqlRowSet columnDefinitions = jdbcTemplate.queryForRowSet(sql); + if (columnDefinitions.next()) { + return columnDefinitions; + } else { + throw new IllegalArgumentException("Table " + tableName + " is not found"); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/DatabaseAwareMigrationContextProvider.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/DatabaseAwareMigrationContextProvider.java index 2f27b0820..580e6aba7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/DatabaseAwareMigrationContextProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/DatabaseAwareMigrationContextProvider.java @@ -19,7 +19,6 @@ package org.apache.fineract.infrastructure.core.service.migration; import java.util.Map; -import javax.sql.DataSource; import org.apache.fineract.infrastructure.core.service.database.DatabaseType; import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; import org.springframework.beans.factory.annotation.Autowired; @@ -28,7 +27,7 @@ import org.springframework.stereotype.Component; @Component public class DatabaseAwareMigrationContextProvider { - private final Map contextMapping = Map.of(DatabaseType.MYSQL, "mysql"); + private final Map contextMapping = Map.of(DatabaseType.MYSQL, "mysql", DatabaseType.POSTGRESQL, "postgresql"); private final DatabaseTypeResolver databaseTypeResolver; @@ -37,8 +36,8 @@ public class DatabaseAwareMigrationContextProvider { this.databaseTypeResolver = databaseTypeResolver; } - public String provide(DataSource dataSource) { - DatabaseType databaseType = databaseTypeResolver.databaseType(dataSource); + public String provide() { + DatabaseType databaseType = databaseTypeResolver.databaseType(); String context = contextMapping.get(databaseType); if (context == null) { throw new IllegalStateException("Database is not supported"); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/ExtendedSpringLiquibaseFactory.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/ExtendedSpringLiquibaseFactory.java index 4c40e35d7..85de2b3c6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/ExtendedSpringLiquibaseFactory.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/ExtendedSpringLiquibaseFactory.java @@ -43,7 +43,7 @@ public class ExtendedSpringLiquibaseFactory { } public ExtendedSpringLiquibase create(DataSource dataSource, String... contexts) { - String databaseContext = databaseAwareMigrationContextProvider.provide(dataSource); + String databaseContext = databaseAwareMigrationContextProvider.provide(); return new ExtendedSpringLiquibaseBuilder(liquibaseProperties).withDataSource(dataSource).withResourceLoader(resourceLoader) .withContexts(contexts).withContexts(environment.getActiveProfiles()).withContext(databaseContext).build(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/data/CreditReportReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/data/CreditReportReadPlatformServiceImpl.java index 9209336cc..7e52c03e3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/data/CreditReportReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/data/CreditReportReadPlatformServiceImpl.java @@ -44,7 +44,7 @@ public class CreditReportReadPlatformServiceImpl implements CreditReportReadPlat private static final class CreditReportDataMapper implements RowMapper { public String schema() { - return " c.id as id, c.creditBureauId as creditBureauId , c.nationalId as nationalId from m_creditreport c "; + return " c.id as id, c.credit_bureau_id as creditBureauId , c.national_id as nationalId from m_creditreport c "; } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureau.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureau.java index 565dbbc3e..2bf30d301 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureau.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureau.java @@ -19,6 +19,7 @@ package org.apache.fineract.infrastructure.creditbureau.domain; import java.util.List; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import org.apache.fineract.infrastructure.core.api.JsonCommand; @@ -34,6 +35,7 @@ public class CreditBureau extends AbstractPersistableCustom { private String country; + @Column(name = "implementation_key") private String implementationKey; public CreditBureau(String name, String product, String country, String implementationKey, diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauLoanProductMapping.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauLoanProductMapping.java index 41b4de860..a7a603f0f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauLoanProductMapping.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauLoanProductMapping.java @@ -33,15 +33,16 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; @Table(name = "m_creditbureau_loanproduct_mapping") public class CreditBureauLoanProductMapping extends AbstractPersistableCustom { - @Column(name = "is_CreditCheck_Mandatory") + @Column(name = "is_credit_check_mandatory") private boolean isCreditCheckMandatory; - @Column(name = "skip_CreditCheck_in_Failure") + @Column(name = "skip_credit_check_in_failure") private boolean skipCreditCheckInFailure; - @Column(name = "stale_Period") + @Column(name = "stale_period") private int stalePeriod; + @Column(name = "is_active") private boolean isActive; @ManyToOne diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauToken.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauToken.java index 34843af50..3734dff92 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauToken.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditBureauToken.java @@ -43,16 +43,16 @@ public class CreditBureauToken extends AbstractPersistableCustom { @Column(name = "token") private String accessToken; - @Column(name = "tokenType") + @Column(name = "token_type") private String tokenType; - @Column(name = "expiresIn") + @Column(name = "expires_in") private String expiresIn; @Column(name = "issued") private String issued; - @Column(name = "expiryDate") + @Column(name = "expiry_date") private Date expires; public static CreditBureauToken fromJson(final JsonCommand command) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditReport.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditReport.java index c0172889e..0a0a8d8ad 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditReport.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/CreditReport.java @@ -29,14 +29,14 @@ import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; @Table(name = "m_creditreport") public final class CreditReport extends AbstractPersistableCustom { - @Column(name = "creditBureauId") + @Column(name = "credit_bureau_id") private Long creditBureauId; - @Column(name = "nationalId") + @Column(name = "national_id") private String nationalId; @Basic(fetch = FetchType.LAZY) - @Column(name = "creditReports") + @Column(name = "credit_reports") private byte[] creditReports; private CreditReport() {} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/OrganisationCreditBureau.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/OrganisationCreditBureau.java index ee9833876..b9ebda9d3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/OrganisationCreditBureau.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/domain/OrganisationCreditBureau.java @@ -21,6 +21,7 @@ package org.apache.fineract.infrastructure.creditbureau.domain; import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @@ -37,6 +38,7 @@ public class OrganisationCreditBureau extends AbstractPersistableCustom { @OneToOne private CreditBureau creditbureau; + @Column(name = "is_active") private boolean isActive; @OneToMany(mappedBy = "organisation_creditbureau", cascade = CascadeType.ALL) diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauLoanProductMappingReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauLoanProductMappingReadPlatformServiceImpl.java index 06ce1ca6c..a7b38e497 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauLoanProductMappingReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauLoanProductMappingReadPlatformServiceImpl.java @@ -48,7 +48,7 @@ public class CreditBureauLoanProductMappingReadPlatformServiceImpl implements Cr return "cblp.id as mappingId, cblp.organisation_creditbureau_id as orgcbId," + "orgcb.alias as alias,concat( cb.product,' - ' ,cb.name,' - ',cb.country) as creditbureau," + "cblp.loan_product_id as lpId,lp.name as loan_product_name,cblp.is_creditcheck_mandatory as crCheck,cblp.skip_creditcheck_in_failure as skipcheck," - + "cblp.stale_period as staleperiod,cblp.isActive as isActive from" + + "cblp.stale_period as staleperiod,cblp.is_active as isActive from" + " m_creditbureau_loanproduct_mapping cblp, m_organisation_creditbureau orgcb,m_product_loan lp,m_creditbureau cb" + " where cblp.organisation_creditbureau_id=orgcb.id and cblp.loan_product_id=lp.id and orgcb.creditbureau_id=cb.id"; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauReadPlatformServiceImpl.java index 6ca410fc9..2f33d6e3b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/CreditBureauReadPlatformServiceImpl.java @@ -45,7 +45,7 @@ public class CreditBureauReadPlatformServiceImpl implements CreditBureauReadPlat public String schema() { return "cb.id as creditBureauID,cb.name as creditBureauName,cb.product as creditBureauProduct," - + "cb.country as country,concat(cb.product,' - ',cb.name,' - ',cb.country) as cbSummary,cb.implementationKey as implementationKey from m_creditbureau cb"; + + "cb.country as country,concat(cb.product,' - ',cb.name,' - ',cb.country) as cbSummary,cb.implementation_key as implementationKey from m_creditbureau cb"; } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/OrganisationCreditBureauReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/OrganisationCreditBureauReadPlatformServiceImpl.java index a65962cd9..2a92d0975 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/OrganisationCreditBureauReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/OrganisationCreditBureauReadPlatformServiceImpl.java @@ -46,7 +46,7 @@ public class OrganisationCreditBureauReadPlatformServiceImpl implements Organisa public String schema() { return "ocb.id as orgCbId,ocb.alias as orgCbAlias,cb.name as creditbureauName,cb.product as creditbureauProduct,cb.country as creditbureauCountry," + "concat(cb.product,' - ',cb.name,' - ',cb.country) as CreditBureauSummary," - + "ocb.creditbureau_id as cbid,ocb.isActive as isActive" + + "ocb.creditbureau_id as cbid,ocb.is_active as isActive" + " from m_organisation_creditbureau ocb,m_creditbureau cb where ocb.creditbureau_id=cb.id"; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java index 32023ab4e..485d46075 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java @@ -166,7 +166,8 @@ public final class ResultsetColumnHeaderData implements Serializable { } private boolean isDateTime() { - return "datetime".equalsIgnoreCase(this.columnType); + return "datetime".equalsIgnoreCase(this.columnType) || "timestamp without time zone".equalsIgnoreCase(this.columnType) + || "timestamptz".equalsIgnoreCase(this.columnType) || "timestamp with time zone".equalsIgnoreCase(this.columnType); } public boolean isString() { @@ -174,7 +175,7 @@ public final class ResultsetColumnHeaderData implements Serializable { } private boolean isChar() { - return "char".equalsIgnoreCase(this.columnType); + return "char".equalsIgnoreCase(this.columnType) || "CHARACTER VARYING".equalsIgnoreCase(this.columnType); } private boolean isVarchar() { @@ -190,7 +191,7 @@ public final class ResultsetColumnHeaderData implements Serializable { } private boolean isInteger() { - return "integer".equalsIgnoreCase(this.columnType); + return "integer".equalsIgnoreCase(this.columnType) || "int4".equalsIgnoreCase(this.columnType); } private boolean isSmallInt() { @@ -206,7 +207,7 @@ public final class ResultsetColumnHeaderData implements Serializable { } private boolean isBigInt() { - return "bigint".equalsIgnoreCase(this.columnType); + return "bigint".equalsIgnoreCase(this.columnType) || "int8".equalsIgnoreCase(this.columnType); } private boolean isLong() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadPlatformServiceImpl.java index a8c44ae43..408b06c50 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadPlatformServiceImpl.java @@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.dataqueries.data.DatatableCheckStatusData; import org.apache.fineract.infrastructure.dataqueries.data.DatatableChecksData; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; @@ -52,35 +53,39 @@ import org.springframework.stereotype.Service; public class EntityDatatableChecksReadPlatformServiceImpl implements EntityDatatableChecksReadService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final RegisterDataTableMapper registerDataTableMapper; private final EntityDataTableChecksMapper entityDataTableChecksMapper; private final EntityDatatableChecksRepository entityDatatableChecksRepository; private final ReadWriteNonCoreDataService readWriteNonCoreDataService; private final LoanProductReadPlatformService loanProductReadPlatformService; private final SavingsProductReadPlatformService savingsProductReadPlatformService; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; @Autowired public EntityDatatableChecksReadPlatformServiceImpl(final RoutingDataSource dataSource, final LoanProductReadPlatformService loanProductReadPlatformService, final SavingsProductReadPlatformService savingsProductReadPlatformService, final EntityDatatableChecksRepository entityDatatableChecksRepository, - final ReadWriteNonCoreDataService readWriteNonCoreDataService) { + final ReadWriteNonCoreDataService readWriteNonCoreDataService, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; this.registerDataTableMapper = new RegisterDataTableMapper(); this.entityDataTableChecksMapper = new EntityDataTableChecksMapper(); this.loanProductReadPlatformService = loanProductReadPlatformService; this.savingsProductReadPlatformService = savingsProductReadPlatformService; this.entityDatatableChecksRepository = entityDatatableChecksRepository; this.readWriteNonCoreDataService = readWriteNonCoreDataService; + this.paginationHelper = paginationHelper; } @Override public Page retrieveAll(SearchParameters searchParameters, final Long status, final String entity, final Long productId) { final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.entityDataTableChecksMapper.schema()); if (status != null || entity != null || productId != null) { @@ -101,15 +106,16 @@ public class EntityDatatableChecksReadPlatformServiceImpl implements EntityDatat sqlBuilder.append(" and t.product_id = ? "); paramList.add(productId); } + if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(jdbcTemplate, sqlCountRows, sqlBuilder.toString(), paramList.toArray(), - entityDataTableChecksMapper); + return this.paginationHelper.fetchPage(jdbcTemplate, sqlBuilder.toString(), paramList.toArray(), entityDataTableChecksMapper); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java index 8c11f880e..7a3956a16 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java @@ -26,6 +26,9 @@ import java.util.List; import javax.sql.DataSource; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseIndependentQueryService; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData; import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData; import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnValueData; @@ -45,11 +48,18 @@ public class GenericDataServiceImpl implements GenericDataService { private final JdbcTemplate jdbcTemplate; private final DataSource dataSource; + private final DatabaseSpecificSQLGenerator sqlGenerator; + private final DatabaseTypeResolver databaseTypeResolver; + private final DatabaseIndependentQueryService databaseIndependentQueryService; private static final Logger LOG = LoggerFactory.getLogger(GenericDataServiceImpl.class); @Autowired - public GenericDataServiceImpl(final RoutingDataSource dataSource) { + public GenericDataServiceImpl(final RoutingDataSource dataSource, DatabaseSpecificSQLGenerator sqlGenerator, + DatabaseTypeResolver databaseTypeResolver, DatabaseIndependentQueryService databaseIndependentQueryService) { this.dataSource = dataSource; + this.sqlGenerator = sqlGenerator; + this.databaseTypeResolver = databaseTypeResolver; + this.databaseIndependentQueryService = databaseIndependentQueryService; this.jdbcTemplate = new JdbcTemplate(this.dataSource); } @@ -205,14 +215,14 @@ public class GenericDataServiceImpl implements GenericDataService { columnDefinitions.beforeFirst(); while (columnDefinitions.next()) { - final String columnName = columnDefinitions.getString("COLUMN_NAME"); - final String isNullable = columnDefinitions.getString("IS_NULLABLE"); - final String isPrimaryKey = columnDefinitions.getString("COLUMN_KEY"); - final String columnType = columnDefinitions.getString("DATA_TYPE"); - final Long columnLength = columnDefinitions.getLong("CHARACTER_MAXIMUM_LENGTH"); + final String columnName = columnDefinitions.getString(1); + final String isNullable = columnDefinitions.getString(2); + final String isPrimaryKey = columnDefinitions.getString(5); + final String columnType = columnDefinitions.getString(3); + final Long columnLength = columnDefinitions.getLong(4); - final boolean columnNullable = "YES".equalsIgnoreCase(isNullable); - final boolean columnIsPrimaryKey = "PRI".equalsIgnoreCase(isPrimaryKey); + final boolean columnNullable = "YES".equalsIgnoreCase(isNullable) || "TRUE".equalsIgnoreCase(isNullable); + final boolean columnIsPrimaryKey = "PRI".equalsIgnoreCase(isPrimaryKey) || "TRUE".equalsIgnoreCase(isPrimaryKey); List columnValues = new ArrayList<>(); String codeName = null; @@ -295,18 +305,13 @@ public class GenericDataServiceImpl implements GenericDataService { return columnValues; } + @SuppressWarnings("AvoidHidingCauseException") private SqlRowSet getDatatableMetaData(final String datatable) { - - final String sql = "select COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, COLUMN_KEY" - + " from INFORMATION_SCHEMA.COLUMNS " + " where TABLE_SCHEMA = schema() and TABLE_NAME = '" + datatable - + "'order by ORDINAL_POSITION"; - - final SqlRowSet columnDefinitions = this.jdbcTemplate.queryForRowSet(sql); - if (columnDefinitions.next()) { - return columnDefinitions; + try { + return databaseIndependentQueryService.getTableColumns(dataSource, datatable); + } catch (IllegalArgumentException e) { + throw new DatatableNotFoundException(datatable); } - - throw new DatatableNotFoundException(datatable); } private SqlRowSet getDatatableCodeData(final String datatable, final String columnName) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java index 2f39013a2..58982f794 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java @@ -56,6 +56,8 @@ import org.apache.fineract.infrastructure.core.serialization.DatatableCommandFro import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; import org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant; import org.apache.fineract.infrastructure.dataqueries.data.DataTableValidator; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; @@ -92,15 +94,22 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ private static final String CODE_VALUES_TABLE = "m_code_value"; private static final Logger LOG = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class); + + // TODO: Extract these types out of here private static final ImmutableMap apiTypeToMySQL = ImmutableMap.builder().put("string", "VARCHAR") .put("number", "INT").put("boolean", "BIT").put("decimal", "DECIMAL").put("date", "DATE").put("datetime", "DATETIME") .put("text", "TEXT").put("dropdown", "INT").build(); + private static final ImmutableMap apiTypeToPostgreSQL = ImmutableMap.builder().put("string", "VARCHAR") + .put("number", "INT").put("boolean", "BIT").put("decimal", "DECIMAL").put("date", "DATE").put("datetime", "TIMESTAMP") + .put("text", "TEXT").put("dropdown", "INT").build(); private static final List stringDataTypes = Arrays.asList("char", "varchar", "blob", "text", "tinyblob", "tinytext", "mediumblob", "mediumtext", "longblob", "longtext"); private final JdbcTemplate jdbcTemplate; private final DataSource dataSource; + private final DatabaseTypeResolver databaseTypeResolver; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PlatformSecurityContext context; private final FromJsonHelper fromJsonHelper; private final JsonParserHelper helper; @@ -120,8 +129,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ final FromJsonHelper fromJsonHelper, final GenericDataService genericDataService, final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer, final CodeReadPlatformService codeReadPlatformService, final ConfigurationDomainService configurationDomainService, final DataTableValidator dataTableValidator, - final ColumnValidator columnValidator) { + final ColumnValidator columnValidator, DatabaseTypeResolver databaseTypeResolver, DatabaseSpecificSQLGenerator sqlGenerator) { this.dataSource = dataSource; + this.databaseTypeResolver = databaseTypeResolver; + this.sqlGenerator = sqlGenerator; this.jdbcTemplate = new JdbcTemplate(this.dataSource); this.context = context; this.fromJsonHelper = fromJsonHelper; @@ -487,8 +498,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } private void assertDataTableExists(final String datatableName) { - final String sql = "select (CASE WHEN exists (select 1 from information_schema.tables where table_schema = schema()" - + " and table_name = ?) THEN 'true' ELSE 'false' END)"; + final String sql = "select (CASE WHEN exists (select 1 from information_schema.tables where table_schema = " + + sqlGenerator.currentSchema() + " and table_name = ?) THEN 'true' ELSE 'false' END)"; final String dataTableExistsString = this.jdbcTemplate.queryForObject(sql, String.class, new Object[] { datatableName }); final boolean dataTableExists = Boolean.valueOf(dataTableExistsString); if (!dataTableExists) { @@ -532,15 +543,24 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (StringUtils.isNotBlank(code)) { if (isConstraintApproach) { codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getCodeId()); - constrainBuilder.append(", CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(name).append("` ") - .append("FOREIGN KEY (`" + name + "`) ").append("REFERENCES `").append(CODE_VALUES_TABLE).append("` (`id`)"); + String fkName = "fk_" + dataTableNameAlias + "_" + name; + constrainBuilder.append(", CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ") + .append("FOREIGN KEY (" + sqlGenerator.escape(name) + ") ").append("REFERENCES ") + .append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (id)"); } else { name = datatableColumnNameToCodeValueName(name, code); } } - final String mysqlType = apiTypeToMySQL.get(type); - sqlBuilder = sqlBuilder.append("`" + name + "` " + mysqlType); + final String dataType; + if (databaseTypeResolver.isMySQL()) { + dataType = apiTypeToMySQL.get(type); + } else if (databaseTypeResolver.isPostgreSQL()) { + dataType = apiTypeToPostgreSQL.get(type); + } else { + throw new IllegalStateException("Current database is not supported"); + } + sqlBuilder = sqlBuilder.append(sqlGenerator.escape(name) + " " + dataType); if (type != null) { if (type.equalsIgnoreCase("String")) { @@ -599,12 +619,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ StringBuilder sqlBuilder = new StringBuilder(); final StringBuilder constrainBuilder = new StringBuilder(); final Map codeMappings = new HashMap<>(); - sqlBuilder = sqlBuilder.append("CREATE TABLE `" + datatableName + "` ("); + sqlBuilder = sqlBuilder.append("CREATE TABLE " + sqlGenerator.escape(datatableName) + " ("); if (multiRow) { - sqlBuilder = sqlBuilder.append("`id` BIGINT NOT NULL AUTO_INCREMENT, ").append("`" + fkColumnName + "` BIGINT NOT NULL, "); + sqlBuilder = sqlBuilder.append("id BIGINT NOT NULL AUTO_INCREMENT, ") + .append(sqlGenerator.escape(fkColumnName) + " BIGINT NOT NULL, "); } else { - sqlBuilder = sqlBuilder.append("`" + fkColumnName + "` BIGINT NOT NULL, "); + sqlBuilder = sqlBuilder.append(sqlGenerator.escape(fkColumnName) + " BIGINT NOT NULL, "); } for (final JsonElement column : columns) { @@ -615,19 +636,26 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ // Remove trailing comma and space sqlBuilder = sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length()); + String fullFkName = "fk_" + fkName; if (multiRow) { - sqlBuilder = sqlBuilder.append(", PRIMARY KEY (`id`)") - .append(", KEY `fk_" + apptableName.substring(2) + "_id` (`" + fkColumnName + "`)") - .append(", CONSTRAINT `fk_" + fkName + "` ").append("FOREIGN KEY (`" + fkColumnName + "`) ") - .append("REFERENCES `" + actualAppTableName + "` (`id`)"); + sqlBuilder = sqlBuilder.append(", PRIMARY KEY (id)") + .append(", KEY " + sqlGenerator.escape("fk_" + apptableName.substring(2) + "_id") + " (" + + sqlGenerator.escape(fkColumnName) + ")") + .append(", CONSTRAINT " + sqlGenerator.escape(fullFkName) + " ") + .append("FOREIGN KEY (" + sqlGenerator.escape(fkColumnName) + ") ") + .append("REFERENCES " + sqlGenerator.escape(actualAppTableName) + " (id)"); } else { - sqlBuilder = sqlBuilder.append(", PRIMARY KEY (`" + fkColumnName + "`)").append(", CONSTRAINT `fk_" + fkName + "` ") - .append("FOREIGN KEY (`" + fkColumnName + "`) ").append("REFERENCES `" + actualAppTableName + "` (`id`)"); + sqlBuilder = sqlBuilder.append(", PRIMARY KEY (" + sqlGenerator.escape(fkColumnName) + ")") + .append(", CONSTRAINT " + sqlGenerator.escape(fullFkName) + " ") + .append("FOREIGN KEY (" + sqlGenerator.escape(fkColumnName) + ") ") + .append("REFERENCES " + sqlGenerator.escape(actualAppTableName) + " (id)"); } sqlBuilder.append(constrainBuilder); - - sqlBuilder = sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;"); + sqlBuilder.append(")"); + if (databaseTypeResolver.isMySQL()) { + sqlBuilder.append(" ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;"); + } this.jdbcTemplate.execute(sqlBuilder.toString()); registerDatatable(datatableName, apptableName, entitySubType); @@ -686,32 +714,33 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (StringUtils.isBlank(newName)) { newName = name; } + String fkName = "fk_" + dataTableNameAlias + "_" + name; + String newFkName = "fk_" + dataTableNameAlias + "_" + newName; if (!StringUtils.equalsIgnoreCase(code, newCode) || !StringUtils.equalsIgnoreCase(name, newName)) { if (StringUtils.equalsIgnoreCase(code, newCode)) { final int codeId = getCodeIdForColumn(dataTableNameAlias, name); if (codeId > 0) { removeMappings.add(dataTableNameAlias + "_" + name); - constrainBuilder.append(", DROP FOREIGN KEY `fk_").append(dataTableNameAlias).append("_").append(name).append("` "); + constrainBuilder.append(", DROP FOREIGN KEY ").append(sqlGenerator.escape(fkName)).append(" "); codeMappings.put(dataTableNameAlias + "_" + newName, (long) codeId); - constrainBuilder.append(",ADD CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(newName).append("` ") - .append("FOREIGN KEY (`" + newName + "`) ").append("REFERENCES `").append(CODE_VALUES_TABLE) - .append("` (`id`)"); + constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(newFkName)).append(" ") + .append("FOREIGN KEY (" + sqlGenerator.escape(newName) + ") ").append("REFERENCES ") + .append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (id)"); } } else { if (code != null) { removeMappings.add(dataTableNameAlias + "_" + name); if (newCode == null || !StringUtils.equalsIgnoreCase(name, newName)) { - constrainBuilder.append(", DROP FOREIGN KEY `fk_").append(dataTableNameAlias).append("_").append(name) - .append("` "); + constrainBuilder.append(", DROP FOREIGN KEY ").append(sqlGenerator.escape(fkName)).append(" "); } } if (newCode != null) { codeMappings.put(dataTableNameAlias + "_" + newName, this.codeReadPlatformService.retriveCode(newCode).getCodeId()); if (code == null || !StringUtils.equalsIgnoreCase(name, newName)) { - constrainBuilder.append(",ADD CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(newName) - .append("` ").append("FOREIGN KEY (`" + newName + "`) ").append("REFERENCES `") - .append(CODE_VALUES_TABLE).append("` (`id`)"); + constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(newFkName)).append(" ") + .append("FOREIGN KEY (" + sqlGenerator.escape(newName) + ") ").append("REFERENCES ") + .append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (id)"); } } } @@ -735,7 +764,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ length = mapColumnNameDefinition.get(name).getColumnLength().intValue(); } - sqlBuilder = sqlBuilder.append(", CHANGE `" + name + "` `" + newName + "` " + type); + sqlBuilder = sqlBuilder.append(", CHANGE " + sqlGenerator.escape(name) + " " + sqlGenerator.escape(newName) + " " + type); if (length != null && length > 0) { if (type.toLowerCase().equals("decimal")) { sqlBuilder.append("(19,6)"); @@ -751,7 +780,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } if (after != null) { - sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); + sqlBuilder = sqlBuilder.append(" AFTER " + sqlGenerator.escape(after)); } } @@ -781,16 +810,25 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (StringUtils.isNotBlank(code)) { if (isConstraintApproach) { + String fkName = "fk_" + dataTableNameAlias + "_" + name; codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getCodeId()); - constrainBuilder.append(",ADD CONSTRAINT `fk_").append(dataTableNameAlias).append("_").append(name).append("` ") - .append("FOREIGN KEY (`" + name + "`) ").append("REFERENCES `").append(CODE_VALUES_TABLE).append("` (`id`)"); + constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ") + .append("FOREIGN KEY (" + sqlGenerator.escape(name) + ") ").append("REFERENCES ") + .append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (id)"); } else { name = datatableColumnNameToCodeValueName(name, code); } } - final String mysqlType = apiTypeToMySQL.get(type); - sqlBuilder = sqlBuilder.append(", ADD `" + name + "` " + mysqlType); + final String dataType; + if (databaseTypeResolver.isMySQL()) { + dataType = apiTypeToMySQL.get(type); + } else if (databaseTypeResolver.isPostgreSQL()) { + dataType = apiTypeToPostgreSQL.get(type); + } else { + throw new IllegalStateException("Current database is not supported"); + } + sqlBuilder = sqlBuilder.append(", ADD " + sqlGenerator.escape(name) + " " + dataType); if (type != null) { if (type.equalsIgnoreCase("String") && length != null) { @@ -809,7 +847,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } if (after != null) { - sqlBuilder = sqlBuilder.append(" AFTER `" + after + "`"); + sqlBuilder = sqlBuilder.append(" AFTER " + sqlGenerator.escape(after)); } } @@ -817,7 +855,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ final StringBuilder constrainBuilder, final List codeMappings) { final String datatableAlias = datatableName.toLowerCase().replaceAll("\\s", "_"); final String name = column.has("name") ? column.get("name").getAsString() : null; - // sqlBuilder = sqlBuilder.append(", DROP COLUMN `" + name + "`"); final StringBuilder findFKSql = new StringBuilder(); findFKSql.append("SELECT count(*)").append("FROM information_schema.TABLE_CONSTRAINTS i") .append(" WHERE i.CONSTRAINT_TYPE = 'FOREIGN KEY'").append(" AND i.TABLE_SCHEMA = DATABASE()") @@ -825,8 +862,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ .append("_").append(name).append("' "); final int count = this.jdbcTemplate.queryForObject(findFKSql.toString(), Integer.class); if (count > 0) { + String fkName = "fk_" + datatableAlias + "_" + name; codeMappings.add(datatableAlias + "_" + name); - constrainBuilder.append(", DROP FOREIGN KEY `fk_").append(datatableAlias).append("_").append(name).append("` "); + constrainBuilder.append(", DROP FOREIGN KEY ").append(sqlGenerator.escape(fkName)).append(" "); } } @@ -875,7 +913,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (StringUtils.isNotEmpty(type)) { if (mandatory && stringDataTypes.contains(type.toLowerCase())) { StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder.append("UPDATE `" + datatableName + "` SET `" + name + "` = '' WHERE `" + name + "` IS NULL"); + sqlBuilder.append("UPDATE " + sqlGenerator.escape(datatableName) + " SET " + sqlGenerator.escape(name) + " = '' WHERE " + + sqlGenerator.escape(name) + " IS NULL"); this.jdbcTemplate.update(sqlBuilder.toString()); } @@ -908,7 +947,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ final boolean isConstraintApproach = this.configurationDomainService.isConstraintApproachEnabledForDatatables(); if (!StringUtils.isBlank(entitySubType)) { - String updateLegalFormSQL = "update `x_registered_table` SET `entity_subtype`='" + entitySubType + String updateLegalFormSQL = "update x_registered_table SET entity_subtype='" + entitySubType + "' WHERE registered_table_name = '" + datatableName + "'"; this.jdbcTemplate.execute(updateLegalFormSQL); } @@ -925,19 +964,28 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ final String newConstraintName = datatableName.toLowerCase().replaceAll("\\s", "_") + "_" + newFKName; StringBuilder sqlBuilder = new StringBuilder(); + String fullOldFk = "fk_" + oldFKName; + String fullOldConstraint = "fk_" + oldConstraintName; + String fullNewFk = "fk_" + newFKName; + String fullNewConstraint = "fk_" + newConstraintName; if (mapColumnNameDefinition.containsKey("id")) { - sqlBuilder = sqlBuilder.append("ALTER TABLE `" + datatableName + "` ").append("DROP KEY `fk_" + oldFKName + "`,") - .append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,") - .append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT NOT NULL,") - .append("ADD KEY `fk_" + newFKName + "` (`" + newFKName + "`),") - .append("ADD CONSTRAINT `fk_" + newConstraintName + "` ").append("FOREIGN KEY (`" + newFKName + "`) ") - .append("REFERENCES `" + actualAppTableName + "` (`id`)"); + sqlBuilder = sqlBuilder.append("ALTER TABLE " + sqlGenerator.escape(datatableName) + " ") + .append("DROP KEY " + sqlGenerator.escape(fullOldFk) + ",") + .append("DROP FOREIGN KEY " + sqlGenerator.escape(fullOldConstraint) + ",") + .append("CHANGE COLUMN " + sqlGenerator.escape(oldFKName) + " " + sqlGenerator.escape(newFKName) + + " BIGINT NOT NULL,") + .append("ADD KEY " + sqlGenerator.escape(fullNewFk) + " (" + sqlGenerator.escape(newFKName) + "),") + .append("ADD CONSTRAINT " + sqlGenerator.escape(fullNewConstraint) + " ") + .append("FOREIGN KEY (" + sqlGenerator.escape(newFKName) + ") ") + .append("REFERENCES " + sqlGenerator.escape(actualAppTableName) + " (id)"); } else { - sqlBuilder = sqlBuilder.append("ALTER TABLE `" + datatableName + "` ") - .append("DROP FOREIGN KEY `fk_" + oldConstraintName + "`,") - .append("CHANGE COLUMN `" + oldFKName + "` `" + newFKName + "` BIGINT NOT NULL,") - .append("ADD CONSTRAINT `fk_" + newConstraintName + "` ").append("FOREIGN KEY (`" + newFKName + "`) ") - .append("REFERENCES `" + actualAppTableName + "` (`id`)"); + sqlBuilder = sqlBuilder.append("ALTER TABLE " + sqlGenerator.escape(datatableName) + " ") + .append("DROP FOREIGN KEY " + sqlGenerator.escape(fullOldConstraint) + ",") + .append("CHANGE COLUMN " + sqlGenerator.escape(oldFKName) + " " + sqlGenerator.escape(newFKName) + + " BIGINT NOT NULL,") + .append("ADD CONSTRAINT " + sqlGenerator.escape(fullNewConstraint) + " ") + .append("FOREIGN KEY (" + sqlGenerator.escape(newFKName) + ") ") + .append("REFERENCES " + sqlGenerator.escape(actualAppTableName) + " (id)"); } this.jdbcTemplate.execute(sqlBuilder.toString()); @@ -956,14 +1004,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ throw new GeneralPlatformDomainRuleException("error.msg.non.empty.datatable.column.cannot.be.deleted", "Non-empty datatable columns can not be deleted."); } - StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE " + sqlGenerator.escape(datatableName)); final StringBuilder constrainBuilder = new StringBuilder(); final List codeMappings = new ArrayList<>(); for (final JsonElement column : dropColumns) { parseDatatableColumnForDrop(column.getAsJsonObject(), sqlBuilder, datatableName, constrainBuilder, codeMappings); } - // Remove the first comma, right after ALTER TABLE `datatable` + // Remove the first comma, right after ALTER TABLE datatable final int indexOfFirstComma = sqlBuilder.indexOf(","); if (indexOfFirstComma != -1) { sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); @@ -974,7 +1022,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } if (addColumns != null) { - StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE " + sqlGenerator.escape(datatableName)); final StringBuilder constrainBuilder = new StringBuilder(); final Map codeMappings = new HashMap<>(); for (final JsonElement column : addColumns) { @@ -987,7 +1035,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ constrainBuilder, codeMappings, isConstraintApproach); } - // Remove the first comma, right after ALTER TABLE `datatable` + // Remove the first comma, right after ALTER TABLE datatable final int indexOfFirstComma = sqlBuilder.indexOf(","); if (indexOfFirstComma != -1) { sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); @@ -998,7 +1046,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } if (changeColumns != null) { - StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE `" + datatableName + "`"); + StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE " + sqlGenerator.escape(datatableName)); final StringBuilder constrainBuilder = new StringBuilder(); final Map codeMappings = new HashMap<>(); final List removeMappings = new ArrayList<>(); @@ -1010,7 +1058,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ constrainBuilder, codeMappings, removeMappings, isConstraintApproach); } - // Remove the first comma, right after ALTER TABLE `datatable` + // Remove the first comma, right after ALTER TABLE datatable final int indexOfFirstComma = sqlBuilder.indexOf(","); if (indexOfFirstComma != -1) { sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); @@ -1090,7 +1138,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } else { sqlArray = new String[1]; } - final String sql = "DROP TABLE `" + datatableName + "`"; + final String sql = "DROP TABLE " + sqlGenerator.escape(datatableName); sqlArray[0] = sql; this.jdbcTemplate.batchUpdate(sqlArray); } catch (final JpaSystemException | DataIntegrityViolationException e) { @@ -1114,7 +1162,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } private int getRowCount(final String datatableName) { - final String sql = "select count(*) from `" + datatableName + "`"; + final String sql = "select count(*) from " + sqlGenerator.escape(datatableName); return this.jdbcTemplate.queryForObject(sql, Integer.class); } @@ -1240,9 +1288,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (id == null) { String whereClause = getFKField(appTable) + " = " + appTableId; SQLInjectionValidator.validateSQLInput(whereClause); - sql = sql + "select * from `" + dataTableName + "` where " + whereClause; + sql = sql + "select * from " + sqlGenerator.escape(dataTableName) + " where " + whereClause; } else { - sql = sql + "select * from `" + dataTableName + "` where id = " + id; + sql = sql + "select * from " + sqlGenerator.escape(dataTableName) + " where id = " + id; } if (StringUtils.isNotBlank(order)) { @@ -1267,9 +1315,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (id == null) { String whereClause = getFKField(appTable) + " = " + appTableId; SQLInjectionValidator.validateSQLInput(whereClause); - sql = sql + "select * from `" + dataTableName + "` where " + whereClause; + sql = sql + "select * from " + sqlGenerator.escape(dataTableName) + " where " + whereClause; } else { - sql = sql + "select * from `" + dataTableName + "` where id = " + id; + sql = sql + "select * from " + sqlGenerator.escape(dataTableName) + " where id = " + id; } final List result = fillDatatableResultSetDataRows(sql); @@ -1328,22 +1376,22 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ * means it relates to an m_client that is in a group (still an m_client account) */ if (appTable.equalsIgnoreCase("m_loan")) { - scopedSQL = "select distinctrow x.* from (" + scopedSQL = "select distinct x.* from (" + " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId from m_loan l " + " join m_client c on c.id = l.client_id " + " join m_office o on o.id = c.office_id and o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'" + " where l.id = " + appTableId + ")" + " union all " + " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId from m_loan l " + " join m_group g on g.id = l.group_id " + " join m_office o on o.id = g.office_id and o.hierarchy like '" - + currentUser.getOffice().getHierarchy() + "%'" + " where l.id = " + appTableId + ")" + " ) x"; + + currentUser.getOffice().getHierarchy() + "%'" + " where l.id = " + appTableId + ")" + " ) as x"; } if (appTable.equalsIgnoreCase("m_savings_account")) { - scopedSQL = "select distinctrow x.* from (" + scopedSQL = "select distinct x.* from (" + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId from m_savings_account s " + " join m_client c on c.id = s.client_id " + " join m_office o on o.id = c.office_id and o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'" + " where s.id = " + appTableId + ")" + " union all " + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId from m_savings_account s " + " join m_group g on g.id = s.group_id " + " join m_office o on o.id = g.office_id and o.hierarchy like '" - + currentUser.getOffice().getHierarchy() + "%'" + " where s.id = " + appTableId + ")" + " ) x"; + + currentUser.getOffice().getHierarchy() + "%'" + " where s.id = " + appTableId + ")" + " ) as x"; } if (appTable.equalsIgnoreCase("m_client")) { scopedSQL = "select o.id as officeId, null as groupId, c.id as clientId, null as savingsId, null as loanId, null as entityId from m_client c " @@ -1484,14 +1532,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } } - columnName = "`" + key + "`"; + columnName = sqlGenerator.escape(key); insertColumns += ", " + columnName; selectColumns += "," + pValueWrite + " as " + columnName; } } - addSql = "insert into `" + datatable + "` (`" + fkName + "` " + insertColumns + ")" + " select " + appTableId + " as id" - + selectColumns; + addSql = "insert into " + sqlGenerator.escape(datatable) + " (" + sqlGenerator.escape(fkName) + " " + insertColumns + ")" + + " select " + appTableId + " as id" + selectColumns; LOG.info("{}", addSql); @@ -1532,16 +1580,16 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ scoresId += pValueWrite + " ,"; } - columnName = "`" + key + "`"; + columnName = sqlGenerator.escape(key); insertColumns += ", " + columnName; selectColumns += "," + pValueWrite + " as " + columnName; } scoresId = scoresId.replaceAll(" ,$", ""); - String vaddSql = "insert into `" + datatable + "` (`" + fkName + "` " + insertColumns + ", `score` )" + " select " + appTableId - + " as id" + selectColumns + " , ( SELECT SUM( code_score ) FROM m_code_value WHERE m_code_value.id IN (" + scoresId - + " ) ) as score"; + String vaddSql = "insert into " + sqlGenerator.escape(datatable) + " (" + sqlGenerator.escape(fkName) + " " + insertColumns + + ", score )" + " select " + appTableId + " as id" + selectColumns + + " , ( SELECT SUM( code_score ) FROM m_code_value WHERE m_code_value.id IN (" + scoresId + " ) ) as score"; LOG.info("{}", vaddSql); @@ -1566,7 +1614,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ String pValueWrite = ""; final String singleQuote = "'"; boolean firstColumn = true; - String sql = "update `" + datatable + "` "; + String sql = "update " + sqlGenerator.escape(datatable) + " "; for (final ResultsetColumnHeaderData pColumnHeader : columnHeaders) { final String key = pColumnHeader.getColumnName(); if (changedColumns.containsKey(key)) { @@ -1588,7 +1636,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ + singleQuote; } } - sql += "`" + key + "` = " + pValueWrite; + sql += sqlGenerator.escape(key) + " = " + pValueWrite; } } @@ -1783,7 +1831,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } paramValue = tmpBoolean.toString(); } else if (columnHeader.isString()) { - if (paramValue.length() > columnHeader.getColumnLength()) { + if (columnHeader.getColumnLength() > 0 && paramValue.length() > columnHeader.getColumnLength()) { final ApiParameterError error = ApiParameterError.parameterError( "validation.msg.datatable.entry.column.exceeds.maxlength", "The column `" + columnHeader.getColumnName() + "` exceeds its defined max-length ", @@ -1801,13 +1849,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ private String getDeleteEntriesSql(final String datatable, final String FKField, final Long appTableId) { - return "delete from `" + datatable + "` where `" + FKField + "` = " + appTableId; + return "delete from " + sqlGenerator.escape(datatable) + " where " + sqlGenerator.escape(FKField) + " = " + appTableId; } private String getDeleteEntrySql(final String datatable, final Long datatableId) { - return "delete from `" + datatable + "` where `id` = " + datatableId; + return "delete from " + sqlGenerator.escape(datatable) + " where id = " + datatableId; } @@ -1841,17 +1889,17 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ @Override public Long countDatatableEntries(final String datatableName, final Long appTableId, String foreignKeyColumn) { - final String sqlString = "SELECT COUNT(`" + foreignKeyColumn + "`) FROM `" + datatableName + "` WHERE `" + foreignKeyColumn + "`=" - + appTableId; + final String sqlString = "SELECT COUNT(" + sqlGenerator.escape(foreignKeyColumn) + ") FROM " + sqlGenerator.escape(datatableName) + + " WHERE " + sqlGenerator.escape(foreignKeyColumn) + "=" + appTableId; final Long count = this.jdbcTemplate.queryForObject(sqlString, Long.class); return count; } public boolean isDatatableAttachedToEntityDatatableCheck(final String datatableName) { StringBuilder builder = new StringBuilder(); - builder.append(" SELECT COUNT(edc.`x_registered_table_name`) FROM `x_registered_table` xrt "); - builder.append(" JOIN m_entity_datatable_check edc ON edc.`x_registered_table_name` = xrt.`registered_table_name`"); - builder.append(" WHERE edc.`x_registered_table_name` = '" + datatableName + "'"); + builder.append(" SELECT COUNT(edc.x_registered_table_name) FROM x_registered_table xrt "); + builder.append(" JOIN m_entity_datatable_check edc ON edc.x_registered_table_name = xrt.registered_table_name"); + builder.append(" WHERE edc.x_registered_table_name = '" + datatableName + "'"); final Long count = this.jdbcTemplate.queryForObject(builder.toString(), Long.class); return count > 0 ? true : false; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java index a00f38af3..6139cbebd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.jobs.data.JobDetailData; import org.apache.fineract.infrastructure.jobs.data.JobDetailHistoryData; import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException; @@ -36,24 +37,30 @@ import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional(readOnly = true) public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerReadService { private final JdbcTemplate jdbcTemplate; private final ColumnValidator columnValidator; + private final DatabaseSpecificSQLGenerator sqlGenerator; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; @Autowired - public SchedulerJobRunnerReadServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator) { + public SchedulerJobRunnerReadServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.columnValidator = columnValidator; + this.sqlGenerator = sqlGenerator; + this.paginationHelper = paginationHelper; } @Override public List findAllJobDeatils() { - final JobDetailMapper detailMapper = new JobDetailMapper(); + final JobDetailMapper detailMapper = new JobDetailMapper(sqlGenerator); final String sql = detailMapper.schema(); final List JobDeatils = this.jdbcTemplate.query(sql, detailMapper, new Object[] {}); return JobDeatils; @@ -63,7 +70,7 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead @Override public JobDetailData retrieveOne(final Long jobId) { try { - final JobDetailMapper detailMapper = new JobDetailMapper(); + final JobDetailMapper detailMapper = new JobDetailMapper(sqlGenerator); final String sql = detailMapper.schema() + " where job.id=?"; return this.jdbcTemplate.queryForObject(sql, detailMapper, new Object[] { jobId }); } catch (final EmptyResultDataAccessException e) { @@ -76,9 +83,9 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead if (!isJobExist(jobId)) { throw new JobNotFoundException(String.valueOf(jobId)); } - final JobHistoryMapper jobHistoryMapper = new JobHistoryMapper(); + final JobHistoryMapper jobHistoryMapper = new JobHistoryMapper(sqlGenerator); final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(jobHistoryMapper.schema()); sqlBuilder.append(" where job.id=?"); if (searchParameters.isOrderByRequested()) { @@ -91,15 +98,15 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), new Object[] { jobId }, - jobHistoryMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), new Object[] { jobId }, jobHistoryMapper); } @Override @@ -131,10 +138,16 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead private static final class JobDetailMapper implements RowMapper { - private final StringBuilder sqlBuilder = new StringBuilder("select").append( - " job.id,job.display_name as displayName,job.next_run_time as nextRunTime,job.initializing_errorlog as initializingError,job.cron_expression as cronExpression,job.is_active as active,job.currently_running as currentlyRunning,") - .append(" runHistory.version,runHistory.start_time as lastRunStartTime,runHistory.end_time as lastRunEndTime,runHistory.`status`,runHistory.error_message as jobRunErrorMessage,runHistory.trigger_type as triggerType,runHistory.error_log as jobRunErrorLog ") - .append(" from job job left join job_run_history runHistory ON job.id=runHistory.job_id and job.previous_run_start_time=runHistory.start_time "); + private final StringBuilder sqlBuilder; + + JobDetailMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + sqlBuilder = new StringBuilder("select").append( + " job.id,job.display_name as displayName,job.next_run_time as nextRunTime,job.initializing_errorlog as initializingError,job.cron_expression as cronExpression,job.is_active as active,job.currently_running as currentlyRunning,") + .append(" runHistory.version,runHistory.start_time as lastRunStartTime,runHistory.end_time as lastRunEndTime,runHistory." + + sqlGenerator.escape("status") + + ",runHistory.error_message as jobRunErrorMessage,runHistory.trigger_type as triggerType,runHistory.error_log as jobRunErrorLog ") + .append(" from job job left join job_run_history runHistory ON job.id=runHistory.job_id and job.previous_run_start_time=runHistory.start_time "); + } public String schema() { return this.sqlBuilder.toString(); @@ -172,9 +185,15 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead private static final class JobHistoryMapper implements RowMapper { - private final StringBuilder sqlBuilder = new StringBuilder(200).append( - " runHistory.version,runHistory.start_time as runStartTime,runHistory.end_time as runEndTime,runHistory.`status`,runHistory.error_message as jobRunErrorMessage,runHistory.trigger_type as triggerType,runHistory.error_log as jobRunErrorLog ") - .append(" from job job join job_run_history runHistory ON job.id=runHistory.job_id"); + private final StringBuilder sqlBuilder; + + JobHistoryMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + sqlBuilder = new StringBuilder(200) + .append(" runHistory.version,runHistory.start_time as runStartTime,runHistory.end_time as runEndTime,runHistory." + + sqlGenerator.escape("status") + + ",runHistory.error_message as jobRunErrorMessage,runHistory.trigger_type as triggerType,runHistory.error_log as jobRunErrorLog ") + .append(" from job job join job_run_history runHistory ON job.id=runHistory.job_id"); + } public String schema() { return this.sqlBuilder.toString(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/openjpa/OpenJpaTransactionManager.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/openjpa/OpenJpaTransactionManager.java new file mode 100644 index 000000000..ae9b83d40 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/openjpa/OpenJpaTransactionManager.java @@ -0,0 +1,42 @@ +/** + * 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.infrastructure.openjpa; + +import javax.persistence.EntityManager; +import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport; +import org.springframework.orm.jpa.EntityManagerHolder; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.transaction.support.DefaultTransactionStatus; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +public class OpenJpaTransactionManager extends JpaTransactionManager { + + @Override + protected void doCommit(DefaultTransactionStatus status) { + JdbcTransactionObjectSupport txObject = (JdbcTransactionObjectSupport) status.getTransaction(); + if (txObject.isReadOnly()) { + EntityManagerHolder holder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(obtainEntityManagerFactory()); + if (holder != null) { + EntityManager entityManager = holder.getEntityManager(); + entityManager.clear(); + } + } + super.doCommit(status); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java index f09f5762c..518c610b7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java @@ -31,6 +31,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.dataqueries.data.ReportData; import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobData; import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat; @@ -49,11 +50,16 @@ public class ReportMailingJobReadPlatformServiceImpl implements ReportMailingJob private final JdbcTemplate jdbcTemplate; private final ColumnValidator columnValidator; + private final DatabaseSpecificSQLGenerator sqlGenerator; + private final PaginationHelper paginationHelper; @Autowired - public ReportMailingJobReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator) { + public ReportMailingJobReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.columnValidator = columnValidator; + this.sqlGenerator = sqlGenerator; + this.paginationHelper = paginationHelper; } @Override @@ -61,9 +67,8 @@ public class ReportMailingJobReadPlatformServiceImpl implements ReportMailingJob final StringBuilder sqlStringBuilder = new StringBuilder(200); final List queryParameters = new ArrayList<>(); final ReportMailingJobMapper mapper = new ReportMailingJobMapper(); - final PaginationHelper paginationHelper = new PaginationHelper<>(); - sqlStringBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlStringBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlStringBuilder.append(mapper.reportMailingJobSchema()); sqlStringBuilder.append(" where rmj.is_deleted = false"); @@ -79,15 +84,15 @@ public class ReportMailingJobReadPlatformServiceImpl implements ReportMailingJob } if (searchParameters.isLimited()) { - sqlStringBuilder.append(" limit ").append(searchParameters.getLimit()); - + sqlStringBuilder.append(" "); if (searchParameters.isOffset()) { - sqlStringBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - return paginationHelper.fetchPage(this.jdbcTemplate, "SELECT FOUND_ROWS()", sqlStringBuilder.toString(), queryParameters.toArray(), - mapper); + return paginationHelper.fetchPage(this.jdbcTemplate, sqlStringBuilder.toString(), queryParameters.toArray(), mapper); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java index 5c83cafae..9f5c65ea2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java @@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobRunHistoryData; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.springframework.beans.factory.annotation.Autowired; @@ -39,15 +40,19 @@ import org.springframework.stereotype.Service; public class ReportMailingJobRunHistoryReadPlatformServiceImpl implements ReportMailingJobRunHistoryReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final ReportMailingJobRunHistoryMapper reportMailingJobRunHistoryMapper; private final ColumnValidator columnValidator; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; @Autowired - public ReportMailingJobRunHistoryReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator) { + public ReportMailingJobRunHistoryReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; this.reportMailingJobRunHistoryMapper = new ReportMailingJobRunHistoryMapper(); this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; } @Override @@ -56,7 +61,7 @@ public class ReportMailingJobRunHistoryReadPlatformServiceImpl implements Report final StringBuilder sqlStringBuilder = new StringBuilder(200); final List queryParameters = new ArrayList<>(); - sqlStringBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlStringBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlStringBuilder.append(this.reportMailingJobRunHistoryMapper.reportMailingJobRunHistorySchema()); if (reportMailingJobId != null) { @@ -74,15 +79,16 @@ public class ReportMailingJobRunHistoryReadPlatformServiceImpl implements Report } if (searchParameters.isLimited()) { - sqlStringBuilder.append(" limit ").append(searchParameters.getLimit()); - + sqlStringBuilder.append(" "); if (searchParameters.isOffset()) { - sqlStringBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - return this.paginationHelper.fetchPage(this.jdbcTemplate, "SELECT FOUND_ROWS()", sqlStringBuilder.toString(), - queryParameters.toArray(), this.reportMailingJobRunHistoryMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlStringBuilder.toString(), queryParameters.toArray(), + this.reportMailingJobRunHistoryMapper); } private static final class ReportMailingJobRunHistoryMapper implements RowMapper { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/service/SmsReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/service/SmsReadPlatformServiceImpl.java index 9593ec7bd..a860fa0e7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/service/SmsReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/sms/service/SmsReadPlatformServiceImpl.java @@ -32,6 +32,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.sms.data.SmsData; import org.apache.fineract.infrastructure.sms.domain.SmsMessageEnumerations; @@ -47,15 +48,19 @@ import org.springframework.stereotype.Service; public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final SmsMapper smsRowMapper; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; private final ColumnValidator columnValidator; @Autowired - public SmsReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator) { + public SmsReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; this.smsRowMapper = new SmsMapper(); this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; } private static final class SmsMapper implements RowMapper { @@ -130,7 +135,7 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { @Override public Collection retrieveAllPending(final Long campaignId, final Integer limit) { - final String sqlPlusLimit = limit > 0 ? " limit 0, " + limit : ""; + final String sqlPlusLimit = limit > 0 ? " " + sqlGenerator.limit(limit) : ""; String sql = "select " + this.smsRowMapper.schema() + " where smo.status_enum = " + SmsMessageStatusType.PENDING.getValue(); if (campaignId != null) { sql += " and smo.campaign_id = " + campaignId; @@ -143,7 +148,7 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { @Override public Collection retrieveAllSent(final Integer limit) { - final String sqlPlusLimit = limit > 0 ? " limit 0, " + limit : ""; + final String sqlPlusLimit = limit > 0 ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select " + this.smsRowMapper.schema() + " where smo.status_enum IN (" + SmsMessageStatusType.WAITING_FOR_DELIVERY_REPORT.getValue() + "," + SmsMessageStatusType.SENT.getValue() + ")" + sqlPlusLimit; @@ -153,7 +158,7 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { @Override public List retrieveExternalIdsOfAllSent(final Integer limit) { - final String sqlPlusLimit = limit > 0 ? " limit 0, " + limit : ""; + final String sqlPlusLimit = limit > 0 ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select external_id from " + this.smsRowMapper.tableName() + " where status_enum = " + SmsMessageStatusType.SENT.getValue() + sqlPlusLimit; @@ -162,18 +167,17 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { @Override public Page retrieveAllWaitingForDeliveryReport(final Integer limit) { - final String sqlPlusLimit = limit > 0 ? " limit 0, " + limit : ""; + final String sqlPlusLimit = limit > 0 ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select id from " + this.smsRowMapper.tableName() + " where status_enum = " + SmsMessageStatusType.WAITING_FOR_DELIVERY_REPORT.getValue() + sqlPlusLimit; - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(jdbcTemplate, sql, sqlCountRows, Long.class); + return this.paginationHelper.fetchPage(jdbcTemplate, sql, Long.class); // (this.jdbcTemplate, sqlCountRows, new Object [] {}, Long.class); // this.jdbcTemplate.queryForList(sql, Long.class); } @Override public List retrieveAllPending(final Integer limit) { - final String sqlPlusLimit = limit > 0 ? " limit 0, " + limit : ""; + final String sqlPlusLimit = limit > 0 ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select external_id from " + this.smsRowMapper.tableName() + " where status_enum = " + SmsMessageStatusType.PENDING.getValue() + sqlPlusLimit; @@ -182,7 +186,7 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { @Override public Collection retrieveAllDelivered(final Integer limit) { - final String sqlPlusLimit = limit > 0 ? " limit 0, " + limit : ""; + final String sqlPlusLimit = limit > 0 ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select " + this.smsRowMapper.schema() + " where smo.status_enum = " + SmsMessageStatusType.DELIVERED.getValue() + sqlPlusLimit; @@ -191,7 +195,7 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { @Override public Collection retrieveAllFailed(final Integer limit) { - final String sqlPlusLimit = limit > 0 ? " limit 0, " + limit : ""; + final String sqlPlusLimit = limit > 0 ? " " + sqlGenerator.limit(limit) : ""; final String sql = "select " + this.smsRowMapper.schema() + " where smo.status_enum = " + SmsMessageStatusType.FAILED.getValue() + sqlPlusLimit; @@ -204,7 +208,7 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { final StringBuilder sqlBuilder = new StringBuilder(200); final Object[] objectArray = new Object[10]; int arrayPos = 0; - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.smsRowMapper.schema()); if (status != null) { sqlBuilder.append(" where smo.campaign_id = ? and smo.status_enum= ? "); @@ -239,14 +243,15 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService { } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, this.smsRowMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.smsRowMapper); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java index 055dfe1da..c2f698476 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java @@ -50,6 +50,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityEx import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.interoperation.data.InteropAccountData; import org.apache.fineract.interoperation.data.InteropIdentifierAccountResponseData; @@ -141,6 +142,7 @@ public class InteropServiceImpl implements InteropService { private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public InteropServiceImpl(PlatformSecurityContext securityContext, InteropDataValidator interopDataValidator, @@ -150,7 +152,7 @@ public class InteropServiceImpl implements InteropService { SavingsHelper savingsHelper, SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper, SavingsAccountDomainService savingsAccountService, final RoutingDataSource dataSource, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, - final DefaultToApiJsonSerializer toApiJsonSerializer) { + final DefaultToApiJsonSerializer toApiJsonSerializer, DatabaseSpecificSQLGenerator sqlGenerator) { this.securityContext = securityContext; this.dataValidator = interopDataValidator; this.savingsAccountRepository = savingsAccountRepository; @@ -166,13 +168,20 @@ public class InteropServiceImpl implements InteropService { this.jdbcTemplate = new JdbcTemplate(dataSource); this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; this.toApiJsonSerializer = toApiJsonSerializer; + this.sqlGenerator = sqlGenerator; } private static final class KycMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + KycMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String schema() { return " country.code_value as nationality, c.date_of_birth as dateOfBirth, c.mobile_no as contactPhone, gender.code_value as gender, c.email_address as email, " - + "kyc.code_value as idType, ci.document_key as idNo, ci.`description` as description, " + + "kyc.code_value as idType, ci.document_key as idNo, ci." + sqlGenerator.escape("description") + " as description, " + "country.code_value as country, a.`address_line_1`, a.`address_line_2`, " + "a.city, state.code_value as stateProvince, a.postal_code as postalCode, c.firstname as firstName, c.middlename as middleName," + "c.lastname as lastName, c.display_name as displayName" + " from " + "m_client c " @@ -512,7 +521,7 @@ public class InteropServiceImpl implements InteropService { Long client_id = savingsAccount.getClient().getId(); try { - final InteropServiceImpl.KycMapper rm = new InteropServiceImpl.KycMapper(); + final InteropServiceImpl.KycMapper rm = new InteropServiceImpl.KycMapper(sqlGenerator); final String sql = "select " + rm.schema() + " where c.id = ?"; final InteropKycData accountKyc = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { client_id }); diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java index d1fb3127a..2d8ad9a5d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java @@ -27,6 +27,7 @@ import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.notification.cache.CacheNotificationResponseHeader; @@ -43,17 +44,20 @@ public class NotificationReadPlatformServiceImpl implements NotificationReadPlat private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; private final ColumnValidator columnValidator; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final NotificationDataRow notificationDataRow = new NotificationDataRow(); private final NotificationMapperRow notificationMapperRow = new NotificationMapperRow(); private HashMap> tenantNotificationResponseHeaderCache = new HashMap<>(); @Autowired public NotificationReadPlatformServiceImpl(final RoutingDataSource dataSource, final PlatformSecurityContext context, - final ColumnValidator columnValidator) { + final ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.context = context; this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -114,8 +118,9 @@ public class NotificationReadPlatformServiceImpl implements NotificationReadPlat @Override public Page getAllUnreadNotifications(final SearchParameters searchParameters) { final Long appUserId = context.authenticatedUser().getId(); - String sql = "SELECT SQL_CALC_FOUND_ROWS ng.id as id, nm.user_id as userId, ng.object_type as objectType, " - + "ng.object_identifier as objectId, ng.actor as actor, ng.action action, ng.notification_content " + String sql = "SELECT " + sqlGenerator.calcFoundRows() + " ng.id as id, nm.user_id as userId, ng.object_type as objectType, " + + "ng.object_identifier as objectId, ng.actor as actor, ng." + sqlGenerator.escape("action") + + " as action, ng.notification_content " + "as content, ng.is_system_generated as isSystemGenerated, nm.created_at as createdAt " + "FROM notification_mapper nm INNER JOIN notification_generator ng ON nm.notification_id = ng.id " + "WHERE nm.user_id = ? AND nm.is_read = false order by nm.created_at desc"; @@ -126,8 +131,9 @@ public class NotificationReadPlatformServiceImpl implements NotificationReadPlat @Override public Page getAllNotifications(SearchParameters searchParameters) { final Long appUserId = context.authenticatedUser().getId(); - String sql = "SELECT SQL_CALC_FOUND_ROWS ng.id as id, nm.user_id as userId, ng.object_type as objectType, " - + "ng.object_identifier as objectId, ng.actor as actor, ng.action action, ng.notification_content " + String sql = "SELECT " + sqlGenerator.calcFoundRows() + " ng.id as id, nm.user_id as userId, ng.object_type as objectType, " + + "ng.object_identifier as objectId, ng.actor as actor, ng." + sqlGenerator.escape("action") + + " as action, ng.notification_content " + "as content, ng.is_system_generated as isSystemGenerated, nm.created_at as createdAt " + "FROM notification_mapper nm INNER JOIN notification_generator ng ON nm.notification_id = ng.id " + "WHERE nm.user_id = ? order by nm.created_at desc"; @@ -149,15 +155,16 @@ public class NotificationReadPlatformServiceImpl implements NotificationReadPlat } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; Object[] params = new Object[] { appUserId }; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), params, this.notificationDataRow); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), params, this.notificationDataRow); } private static final class NotificationMapperRow implements RowMapper { diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicDomainServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicDomainServiceImpl.java index 31dc35e3b..7122bcfa6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicDomainServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicDomainServiceImpl.java @@ -35,9 +35,11 @@ import org.apache.fineract.useradministration.domain.RoleRepository; import org.apache.fineract.useradministration.exception.RoleNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; @Service +@Transactional public class TopicDomainServiceImpl implements TopicDomainService { private final RoleRepository roleRepository; diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeReadPlatformServiceImpl.java index 302fc41d1..ef9c09982 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeReadPlatformServiceImpl.java @@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.monetary.data.CurrencyData; @@ -47,6 +48,7 @@ import org.springframework.stereotype.Service; public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PlatformSecurityContext context; private final CurrencyReadPlatformService currencyReadPlatformService; private final ColumnValidator columnValidator; @@ -55,11 +57,12 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService @Autowired public OfficeReadPlatformServiceImpl(final PlatformSecurityContext context, final CurrencyReadPlatformService currencyReadPlatformService, final RoutingDataSource dataSource, - final ColumnValidator columnValidator) { + final ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.currencyReadPlatformService = currencyReadPlatformService; this.columnValidator = columnValidator; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; } private static final class OfficeMapper implements RowMapper { @@ -105,13 +108,20 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService private static final class OfficeTransactionMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + OfficeTransactionMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String schema() { return " ot.id as id, ot.transaction_date as transactionDate, ot.from_office_id as fromOfficeId, fromoff.name as fromOfficeName, " + " ot.to_office_id as toOfficeId, tooff.name as toOfficeName, ot.transaction_amount as transactionAmount, ot.description as description, " + " ot.currency_code as currencyCode, rc.decimal_places as currencyDigits, rc.currency_multiplesof as inMultiplesOf, " + " rc.name as currencyName, rc.internationalized_name_code as currencyNameCode, rc.display_symbol as currencyDisplaySymbol " + " from m_office_transaction ot " + " left join m_office fromoff on fromoff.id = ot.from_office_id " - + " left join m_office tooff on tooff.id = ot.to_office_id " + " join m_currency rc on rc.`code` = ot.currency_code"; + + " left join m_office tooff on tooff.id = ot.to_office_id " + " join m_currency rc on rc." + + sqlGenerator.escape("code") + " = ot.currency_code"; } @Override @@ -244,7 +254,7 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService final String hierarchy = currentUser.getOffice().getHierarchy(); final String hierarchySearchString = hierarchy + "%"; - final OfficeTransactionMapper rm = new OfficeTransactionMapper(); + final OfficeTransactionMapper rm = new OfficeTransactionMapper(sqlGenerator); final String sql = "select " + rm.schema() + " where (fromoff.hierarchy like ? or tooff.hierarchy like ?) order by ot.transaction_date, ot.id"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java index 46e1ccbf7..cd04c17f2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerManagementReadPlatformServiceImpl.java @@ -33,6 +33,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.monetary.data.CurrencyData; @@ -70,19 +71,23 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement private final OfficeReadPlatformService officeReadPlatformService; private final StaffReadPlatformService staffReadPlatformService; private final CurrencyReadPlatformService currencyReadPlatformService; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final DatabaseSpecificSQLGenerator sqlGenerator; + private final PaginationHelper paginationHelper; private final ColumnValidator columnValidator; @Autowired public TellerManagementReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, final OfficeReadPlatformService officeReadPlatformService, StaffReadPlatformService staffReadPlatformService, - final CurrencyReadPlatformService currencyReadPlatformService, final ColumnValidator columnValidator) { + final CurrencyReadPlatformService currencyReadPlatformService, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.officeReadPlatformService = officeReadPlatformService; this.staffReadPlatformService = staffReadPlatformService; this.currencyReadPlatformService = currencyReadPlatformService; this.columnValidator = columnValidator; + this.sqlGenerator = sqlGenerator; + this.paginationHelper = paginationHelper; } private static final class TellerMapper implements RowMapper { @@ -545,12 +550,13 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement + " and (cli_txn.payment_detail_id IS NULL OR payType.is_cash_payment = true) ) " + " order by created_date "; if (searchParameters.isLimited()) { - sql = sql + " limit " + searchParameters.getLimit(); + sql += " "; if (searchParameters.isOffset()) { - sql = sql + " offset " + searchParameters.getOffset(); + sql += sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()); + } else { + sql += sqlGenerator.limit(searchParameters.getLimit()); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; // return this.jdbcTemplate.query(sql, ctm, new Object[] { cashierId, // currencyCode, hierarchySearchString, cashierId, currencyCode, // hierarchySearchString, cashierId, currencyCode, @@ -558,7 +564,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement // }); Object[] params = new Object[] { cashierId, currencyCode, hierarchySearchString, cashierId, currencyCode, hierarchySearchString, cashierId, currencyCode, hierarchySearchString, cashierId, currencyCode, hierarchySearchString }; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sql, params, ctm); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sql, params, ctm); } private static final class CashierMapper implements RowMapper { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java index 343c91d2c..73686e391 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java @@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.office.data.OfficeData; @@ -62,25 +63,28 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers private final OfficeReadPlatformService officeReadPlatformService; private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; private final ColumnValidator columnValidator; + private final DatabaseSpecificSQLGenerator sqlGenerator; // mapper private final AccountTransfersMapper accountTransfersMapper; // pagination - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Autowired public AccountTransfersReadPlatformServiceImpl(final RoutingDataSource dataSource, final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, - final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, final ColumnValidator columnValidator) { + final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.clientReadPlatformService = clientReadPlatformService; this.officeReadPlatformService = officeReadPlatformService; this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService; this.columnValidator = columnValidator; - + this.sqlGenerator = sqlGenerator; this.accountTransfersMapper = new AccountTransfersMapper(); + this.paginationHelper = paginationHelper; } @Override @@ -215,7 +219,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers public Page retrieveAll(final SearchParameters searchParameters, final Long accountDetailId) { final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.accountTransfersMapper.schema()); Object[] finalObjectArray = {}; if (accountDetailId != null) { @@ -239,9 +243,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, - this.accountTransfersMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.accountTransfersMapper); } @Override @@ -411,7 +413,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers public Page retrieveByStandingInstruction(final Long id, final SearchParameters searchParameters) { final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.accountTransfersMapper.schema()).append( " join m_account_transfer_standing_instructions atsi on atsi.account_transfer_details_id = att.account_transfer_details_id "); sqlBuilder.append(" where atsi.id = ?"); @@ -427,17 +429,17 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } } final Object[] finalObjectArray = { id }; - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, - this.accountTransfersMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.accountTransfersMapper); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java index 28ccc9a53..8e49c0ee3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/PortfolioAccountReadPlatformServiceImpl.java @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.List; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO; @@ -48,11 +49,11 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper; @Autowired - public PortfolioAccountReadPlatformServiceImpl(final RoutingDataSource dataSource) { + public PortfolioAccountReadPlatformServiceImpl(final RoutingDataSource dataSource, DatabaseSpecificSQLGenerator sqlGenerator) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.savingsAccountMapper = new PortfolioSavingsAccountMapper(); this.loanAccountMapper = new PortfolioLoanAccountMapper(); - this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(); + this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator); } @Override @@ -295,7 +296,7 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount private final String schemaSql; - PortfolioLoanAccountRefundByTransferMapper() { + PortfolioLoanAccountRefundByTransferMapper(DatabaseSpecificSQLGenerator sqlGenerator) { final StringBuilder amountQueryString = new StringBuilder(400); amountQueryString.append("(select (SUM(COALESCE(mr.principal_completed_derived, 0)) +"); @@ -304,7 +305,7 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount amountQueryString.append(" SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived"); amountQueryString.append(" from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id"); amountQueryString.append(" where ml.id=? and ml.loan_status_id = 300"); - amountQueryString.append(" and mr.duedate >= CURDATE() group by ml.id having"); + amountQueryString.append(" and mr.duedate >= " + sqlGenerator.currentDate() + " group by ml.id having"); amountQueryString.append(" (SUM(COALESCE(mr.principal_completed_derived, 0)) + "); amountQueryString.append(" SUM(COALESCE(mr.interest_completed_derived, 0)) + "); amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + "); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionHistoryReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionHistoryReadPlatformServiceImpl.java index ecaa85f68..659f591bd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionHistoryReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionHistoryReadPlatformServiceImpl.java @@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.office.data.OfficeData; import org.apache.fineract.portfolio.account.PortfolioAccountType; @@ -50,26 +51,30 @@ import org.springframework.stereotype.Service; public class StandingInstructionHistoryReadPlatformServiceImpl implements StandingInstructionHistoryReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final ColumnValidator columnValidator; // mapper private final StandingInstructionHistoryMapper standingInstructionHistoryMapper; // pagination - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; @Autowired - public StandingInstructionHistoryReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator) { + public StandingInstructionHistoryReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; this.standingInstructionHistoryMapper = new StandingInstructionHistoryMapper(); this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; } @Override public Page retrieveAll(StandingInstructionDTO standingInstructionDTO) { final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.standingInstructionHistoryMapper.schema()); if (standingInstructionDTO.transferType() != null || standingInstructionDTO.clientId() != null || standingInstructionDTO.clientName() != null @@ -149,15 +154,16 @@ public class StandingInstructionHistoryReadPlatformServiceImpl implements Standi } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } final Object[] finalObjectArray = paramObj.toArray(); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.standingInstructionHistoryMapper); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java index 198ec7a6e..66b05255e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionReadPlatformServiceImpl.java @@ -41,6 +41,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.office.data.OfficeData; import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; @@ -76,25 +77,29 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr private final OfficeReadPlatformService officeReadPlatformService; private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; private final DropdownReadPlatformService dropdownReadPlatformService; + private final DatabaseSpecificSQLGenerator sqlGenerator; // mapper private final StandingInstructionMapper standingInstructionMapper; // pagination - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; @Autowired public StandingInstructionReadPlatformServiceImpl(final RoutingDataSource dataSource, final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, - final DropdownReadPlatformService dropdownReadPlatformService, final ColumnValidator columnValidator) { + final DropdownReadPlatformService dropdownReadPlatformService, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.clientReadPlatformService = clientReadPlatformService; this.officeReadPlatformService = officeReadPlatformService; this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService; + this.sqlGenerator = sqlGenerator; this.standingInstructionMapper = new StandingInstructionMapper(); this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; } @Override @@ -257,7 +262,7 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr public Page retrieveAll(final StandingInstructionDTO standingInstructionDTO) { final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.standingInstructionMapper.schema()); if (standingInstructionDTO.transferType() != null || standingInstructionDTO.clientId() != null || standingInstructionDTO.clientName() != null) { @@ -315,16 +320,16 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } final Object[] finalObjectArray = paramObj.toArray(); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, - this.standingInstructionMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.standingInstructionMapper); } @Override @@ -332,9 +337,10 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr final StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append("select "); sqlBuilder.append(this.standingInstructionMapper.schema()); - sqlBuilder.append( - " where atsi.status=? and CURRENT_DATE() >= atsi.valid_from and (atsi.valid_till IS NULL or CURRENT_DATE() < atsi.valid_till) ") - .append(" and (atsi.last_run_date <> CURRENT_DATE() or atsi.last_run_date IS NULL)") + sqlBuilder + .append(" where atsi.status=? and " + sqlGenerator.currentDate() + " >= atsi.valid_from and (atsi.valid_till IS NULL or " + + sqlGenerator.currentDate() + " < atsi.valid_till) ") + .append(" and (atsi.last_run_date <> " + sqlGenerator.currentDate() + " or atsi.last_run_date IS NULL)") .append(" ORDER BY atsi.priority DESC"); return this.jdbcTemplate.query(sqlBuilder.toString(), this.standingInstructionMapper, status); } @@ -354,7 +360,8 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr @Override public StandingInstructionDuesData retriveLoanDuesData(final Long loanId) { final StandingInstructionLoanDuesMapper rm = new StandingInstructionLoanDuesMapper(); - final String sql = "select " + rm.schema() + " where ml.id= ? and ls.duedate <= CURRENT_DATE() and ls.completed_derived <> 1"; + final String sql = "select " + rm.schema() + " where ml.id= ? and ls.duedate <= " + sqlGenerator.currentDate() + + " and ls.completed_derived <> 1"; return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId }); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java index 16919cfcc..89c392f70 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java @@ -40,6 +40,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.apache.fineract.infrastructure.jobs.service.JobName; @@ -85,6 +86,7 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst private final StandingInstructionReadPlatformService standingInstructionReadPlatformService; private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public StandingInstructionWritePlatformServiceImpl(final StandingInstructionDataValidator standingInstructionDataValidator, @@ -92,7 +94,8 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst final AccountTransferDetailRepository accountTransferDetailRepository, final StandingInstructionRepository standingInstructionRepository, final StandingInstructionReadPlatformService standingInstructionReadPlatformService, - final AccountTransfersWritePlatformService accountTransfersWritePlatformService, final RoutingDataSource dataSource) { + final AccountTransfersWritePlatformService accountTransfersWritePlatformService, final RoutingDataSource dataSource, + DatabaseSpecificSQLGenerator sqlGenerator) { this.standingInstructionDataValidator = standingInstructionDataValidator; this.standingInstructionAssembler = standingInstructionAssembler; this.accountTransferDetailRepository = accountTransferDetailRepository; @@ -100,6 +103,7 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst this.standingInstructionReadPlatformService = standingInstructionReadPlatformService; this.accountTransfersWritePlatformService = accountTransfersWritePlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; } @Transactional @@ -261,7 +265,8 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst boolean transferCompleted = true; StringBuilder errorLog = new StringBuilder(); StringBuilder updateQuery = new StringBuilder( - "INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, `status`, amount,execution_time, error_log) VALUES ("); + "INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, " + sqlGenerator.escape("status") + + ", amount,execution_time, error_log) VALUES ("); try { this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } catch (final PlatformApiDataValidationException e) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java index 524016049..5c18cd260 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembers.java @@ -41,10 +41,10 @@ public class ClientFamilyMembers extends AbstractPersistableCustom { @Column(name = "firstname") private String firstName; - @Column(name = "middleName") + @Column(name = "middlename") private String middleName; - @Column(name = "lastName") + @Column(name = "lastname") private String lastName; @Column(name = "qualification") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeReadPlatformServiceImpl.java index abd069aed..ecb8bf135 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeReadPlatformServiceImpl.java @@ -29,6 +29,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.charge.data.ChargeData; @@ -45,16 +46,20 @@ import org.springframework.stereotype.Service; @Service public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlatformService { - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PlatformSecurityContext context; private final ClientChargeMapper clientChargeMapper; @Autowired - public ClientChargeReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource) { + public ClientChargeReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; this.clientChargeMapper = new ClientChargeMapper(); + this.paginationHelper = paginationHelper; } public static final class ClientChargeMapper implements RowMapper { @@ -135,7 +140,7 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat SearchParameters searchParameters) { final ClientChargeMapper rm = new ClientChargeMapper(); final StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS ").append(rm.schema()).append(" where cc.client_id=? "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ").append(rm.schema()).append(" where cc.client_id=? "); // filter for active charges if (status.equalsIgnoreCase(ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ACTIVE)) { @@ -156,14 +161,15 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat // apply limit and offsets if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), new Object[] { clientId }, + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), new Object[] { clientId }, this.clientChargeMapper); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java index 9d58c2602..ea2a15bf8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -43,6 +43,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum; @@ -88,7 +89,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private final CodeValueReadPlatformService codeValueReadPlatformService; private final SavingsProductReadPlatformService savingsProductReadPlatformService; // data mappers - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final ClientMapper clientMapper = new ClientMapper(); private final ClientLookupMapper lookupMapper = new ClientLookupMapper(); private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper(); @@ -110,7 +112,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final ClientFamilyMembersReadPlatformService clientFamilyMembersReadPlatformService, final ConfigurationReadPlatformService configurationReadPlatformService, final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator, - final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper) { + final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.context = context; this.officeReadPlatformService = officeReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); @@ -123,6 +126,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService this.entityDatatableChecksReadService = entityDatatableChecksReadService; this.columnValidator = columnValidator; this.clientCollateralManagementRepositoryWrapper = clientCollateralManagementRepositoryWrapper; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -208,7 +213,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService // } List paramList = new ArrayList<>(Arrays.asList(underHierarchySearchString, underHierarchySearchString)); final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.clientMapper.schema()); sqlBuilder.append(" where (o.hierarchy like ? or transferToOffice.hierarchy like ?) "); @@ -235,15 +240,15 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), paramList.toArray(), - this.clientMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), paramList.toArray(), this.clientMapper); } private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List paramList) { @@ -275,8 +280,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService } if (displayName != null) { - // extraCriteria += " and concat(ifnull(c.firstname, ''), - // if(c.firstname > '',' ', '') , ifnull(c.lastname, '')) like " + // extraCriteria += " and concatcoalesce(c.firstname, ''), + // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like " paramList.add("%" + displayName + "%"); extraCriteria += " and c.display_name like ? "; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java index 658d69334..0cdeb6f50 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java @@ -29,6 +29,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.client.data.ClientTransactionData; import org.apache.fineract.portfolio.client.domain.ClientEnumerations; @@ -46,14 +47,17 @@ import org.springframework.stereotype.Service; public class ClientTransactionReadPlatformServiceImpl implements ClientTransactionReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final ClientTransactionMapper clientTransactionMapper; - private final PaginationHelper paginationHelper; + private final PaginationHelper paginationHelper; @Autowired - public ClientTransactionReadPlatformServiceImpl(final RoutingDataSource dataSource) { + public ClientTransactionReadPlatformServiceImpl(final RoutingDataSource dataSource, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; this.clientTransactionMapper = new ClientTransactionMapper(); - this.paginationHelper = new PaginationHelper<>(); + this.paginationHelper = paginationHelper; } private static final class ClientTransactionMapper implements RowMapper { @@ -137,21 +141,23 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti public Page retrieveAllTransactions(Long clientId, SearchParameters searchParameters) { Object[] parameters = new Object[1]; final StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS ").append(this.clientTransactionMapper.schema()).append(" where c.id = ? "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ").append(this.clientTransactionMapper.schema()) + .append(" where c.id = ? "); parameters[0] = clientId; - final String sqlCountRows = "SELECT FOUND_ROWS()"; sqlBuilder.append(" order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC "); // apply limit and offsets + if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), parameters, - this.clientTransactionMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), parameters, this.clientTransactionMapper); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index 1784822ea..93fed5f87 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.api.JsonQuery; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.calendar.domain.Calendar; @@ -90,12 +91,13 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer; private final CalendarRepositoryWrapper calendarRepositoryWrapper; private final AttendanceDropdownReadPlatformService attendanceDropdownReadPlatformService; - private final MandatorySavingsCollectionsheetExtractor mandatorySavingsExtractor = new MandatorySavingsCollectionsheetExtractor(); + private final MandatorySavingsCollectionsheetExtractor mandatorySavingsExtractor; private final CodeValueReadPlatformService codeValueReadPlatformService; private final PaymentTypeReadPlatformService paymentTypeReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService; private final ConfigurationDomainService configurationDomainService; private final CalendarInstanceRepository calendarInstanceRepository; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public CollectionSheetReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, @@ -106,7 +108,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final CodeValueReadPlatformService codeValueReadPlatformService, final PaymentTypeReadPlatformService paymentTypeReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService, final ConfigurationDomainService configurationDomainService, - final CalendarInstanceRepository calendarInstanceRepository) { + final CalendarInstanceRepository calendarInstanceRepository, DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.namedParameterjdbcTemplate = new NamedParameterJdbcTemplate(dataSource); @@ -119,6 +121,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe this.calendarReadPlatformService = calendarReadPlatformService; this.configurationDomainService = configurationDomainService; this.calendarInstanceRepository = calendarInstanceRepository; + this.sqlGenerator = sqlGenerator; + mandatorySavingsExtractor = new MandatorySavingsCollectionsheetExtractor(sqlGenerator); } /* @@ -215,6 +219,12 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private static final class JLGCollectionSheetFaltDataMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + JLGCollectionSheetFaltDataMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String collectionSheetSchema(final boolean isCenterCollection) { StringBuilder sql = new StringBuilder(400); sql.append("SELECT loandata.*, sum(lc.amount_outstanding_derived) as chargesDue from ") @@ -223,7 +233,9 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe .append("gl.level_name As levelName, ").append("cl.id As clientId, ").append("ln.id As loanId, ") .append("ln.account_no As accountId, ").append("ln.loan_status_id As accountStatusId, ") .append("pl.short_name As productShortName, ").append("ln.product_id As productId, ") - .append("ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, ") + .append("ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMultiplesOf, rc." + + sqlGenerator.escape("name") + + " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, ") .append("(CASE WHEN ln.loan_status_id = 200 THEN ln.principal_amount ELSE null END) As disbursementAmount, ") .append("sum(COALESCE((CASE WHEN ln.loan_status_id = 300 THEN ls.principal_amount ELSE 0.0 END), 0.0) - COALESCE((CASE WHEN ln.loan_status_id = 300 THEN ls.principal_completed_derived ELSE 0.0 END), 0.0)) As principalDue, ") .append("ln.principal_repaid_derived As principalPaid, ") @@ -237,7 +249,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe .append("JOIN m_group_client gc ON gc.group_id = gp.id ").append("JOIN m_client cl ON cl.id = gc.client_id ") .append("LEFT JOIN m_loan ln ON cl.id = ln.client_id and ln.group_id=gp.id AND ln.group_id is not null AND ( ln.loan_status_id = 300 ) ") .append("LEFT JOIN m_product_loan pl ON pl.id = ln.product_id ") - .append("LEFT JOIN m_currency rc on rc.`code` = ln.currency_code ") + .append("LEFT JOIN m_currency rc on rc." + sqlGenerator.escape("code") + " = ln.currency_code ") .append("LEFT JOIN m_loan_repayment_schedule ls ON ls.loan_id = ln.id AND ls.completed_derived = 0 AND ls.duedate <= :dueDate ") .append("left join m_calendar_instance ci on gp.parent_id = ci.entity_id and ci.entity_type_enum =:entityTypeId ") .append("left join m_meeting mt on ci.id = mt.calendar_instance_id and mt.meeting_date =:dueDate ") @@ -354,7 +366,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final String hierarchy = currentUser.getOffice().getHierarchy(); final String officeHierarchy = hierarchy + "%"; - final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper(); + final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper(sqlGenerator); final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", transactionDateStr) .addValue("groupId", group.getId()).addValue("officeHierarchy", officeHierarchy) @@ -457,7 +469,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); final String dueDateStr = df.format(Date.from(transactionDate.atStartOfDay(ZoneId.systemDefault()).toInstant())); - final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper(); + final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper(sqlGenerator); StringBuilder sql = new StringBuilder(mapper.collectionSheetSchema(true)); @@ -487,6 +499,11 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private static final class MandatorySavingsCollectionsheetExtractor implements ResultSetExtractor> { private final GroupSavingsDataMapper groupSavingsDataMapper = new GroupSavingsDataMapper(); + private final DatabaseSpecificSQLGenerator sqlGenerator; + + MandatorySavingsCollectionsheetExtractor(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } public String collectionSheetSchema(final boolean isCenterCollection) { @@ -497,7 +514,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe .append("sa.account_no As accountId, ").append("sa.status_enum As accountStatusId, ") .append("sp.short_name As productShortName, ").append("sp.id As productId, ") .append("sa.currency_code as currencyCode, ").append("sa.currency_digits as currencyDigits, ") - .append("sa.currency_multiplesof as inMultiplesOf, ").append("rc.`name` as currencyName, ") + .append("sa.currency_multiplesof as inMultiplesOf, ").append("rc." + sqlGenerator.escape("name") + " as currencyName, ") .append("rc.display_symbol as currencyDisplaySymbol, ") .append("(CASE WHEN sa.deposit_type_enum=100 THEN 'Saving Deposit' ELSE (CASE WHEN sa.deposit_type_enum=300 THEN 'Recurring Deposit' ELSE 'Current Deposit' END) END) as depositAccountType, ") .append("rc.internationalized_name_code as currencyNameCode, ") @@ -511,7 +528,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe .append("JOIN m_savings_product sp ON sa.product_id=sp.id ") .append("LEFT JOIN m_deposit_account_recurring_detail dard ON sa.id = dard.savings_account_id AND dard.is_mandatory = true AND dard.is_calendar_inherited = true ") .append("LEFT JOIN m_mandatory_savings_schedule mss ON mss.savings_account_id=sa.id AND mss.duedate <= :dueDate ") - .append("LEFT JOIN m_currency rc on rc.`code` = sa.currency_code "); + .append("LEFT JOIN m_currency rc on rc." + sqlGenerator.escape("code") + " = sa.currency_code "); if (isCenterCollection) { sql.append("WHERE gp.parent_id = :centerId "); @@ -680,7 +697,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final boolean checkForStaffId = staffId != null; final IndividualCollectionSheetFaltDataMapper mapper = new IndividualCollectionSheetFaltDataMapper(checkForOfficeId, - checkForStaffId); + checkForStaffId, sqlGenerator); final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", transactionDateStr) .addValue("officeHierarchy", officeHierarchy); @@ -696,7 +713,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe .query(mapper.sqlSchema(), namedParameters, mapper); IndividualMandatorySavingsCollectionsheetExtractor mandatorySavingsExtractor = new IndividualMandatorySavingsCollectionsheetExtractor( - checkForOfficeId, checkForStaffId); + checkForOfficeId, checkForStaffId, sqlGenerator); // mandatory savings data for collection sheet Collection clientData = this.namedParameterjdbcTemplate .query(mandatorySavingsExtractor.collectionSheetSchema(), namedParameters, mandatorySavingsExtractor); @@ -714,15 +731,16 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private final String sql; - IndividualCollectionSheetFaltDataMapper(final boolean checkForOfficeId, final boolean checkforStaffId) { + IndividualCollectionSheetFaltDataMapper(final boolean checkForOfficeId, final boolean checkforStaffId, + DatabaseSpecificSQLGenerator sqlGenerator) { StringBuilder sb = new StringBuilder(); sb.append("SELECT loandata.*, sum(lc.amount_outstanding_derived) as chargesDue "); sb.append("from (SELECT cl.display_name As clientName, "); sb.append("cl.id As clientId, ln.id As loanId, ln.account_no As accountId, ln.loan_status_id As accountStatusId,"); sb.append(" pl.short_name As productShortName, ln.product_id As productId, "); sb.append("ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMultiplesOf, "); - sb.append( - "rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "); + sb.append("rc." + sqlGenerator.escape("name") + + " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "); sb.append("(CASE WHEN ln.loan_status_id = 200 THEN ln.principal_amount ELSE null END) As disbursementAmount, "); sb.append( "sum(COALESCE((CASE WHEN ln.loan_status_id = 300 THEN ls.principal_amount ELSE 0.0 END), 0.0) - COALESCE((CASE WHEN ln.loan_status_id = 300 THEN ls.principal_completed_derived ELSE 0.0 END), 0.0)) As principalDue, "); @@ -737,7 +755,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe sb.append("JOIN m_client cl ON cl.id = ln.client_id "); sb.append("LEFT JOIN m_office of ON of.id = cl.office_id AND of.hierarchy like :officeHierarchy "); sb.append("LEFT JOIN m_product_loan pl ON pl.id = ln.product_id "); - sb.append("LEFT JOIN m_currency rc on rc.`code` = ln.currency_code "); + sb.append("LEFT JOIN m_currency rc on rc." + sqlGenerator.escape("code") + " = ln.currency_code "); sb.append("JOIN m_loan_repayment_schedule ls ON ls.loan_id = ln.id AND ls.completed_derived = 0 AND ls.duedate <= :dueDate "); sb.append("where "); if (checkForOfficeId) { @@ -804,7 +822,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private final String sql; - IndividualMandatorySavingsCollectionsheetExtractor(final boolean checkForOfficeId, final boolean checkforStaffId) { + IndividualMandatorySavingsCollectionsheetExtractor(final boolean checkForOfficeId, final boolean checkforStaffId, + DatabaseSpecificSQLGenerator sqlGenerator) { final StringBuilder sb = new StringBuilder(400); sb.append( @@ -812,8 +831,8 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe sb.append("sa.id As savingsId, sa.account_no As accountId, sa.status_enum As accountStatusId, "); sb.append("sp.short_name As productShortName, sp.id As productId, "); sb.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); - sb.append( - "rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "); + sb.append("rc." + sqlGenerator.escape("name") + + " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "); sb.append("SUM(COALESCE(mss.deposit_amount,0) - coalesce(mss.deposit_amount_completed_derived,0)) as dueAmount "); sb.append("FROM m_savings_account sa "); sb.append("JOIN m_client cl ON cl.id = sa.client_id "); @@ -823,7 +842,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe sb.append( "LEFT JOIN m_mandatory_savings_schedule mss ON mss.savings_account_id=sa.id AND mss.completed_derived = 0 AND mss.duedate <= :dueDate "); sb.append("LEFT JOIN m_office of ON of.id = cl.office_id AND of.hierarchy like :officeHierarchy "); - sb.append("LEFT JOIN m_currency rc on rc.`code` = sa.currency_code "); + sb.append("LEFT JOIN m_currency rc on rc." + sqlGenerator.escape("code") + " = sa.currency_code "); sb.append("WHERE sa.status_enum=300 and sa.group_id is null and sa.deposit_type_enum in (100,300,400) "); sb.append("and (cl.status_enum = 300 or (cl.status_enum = 600 and cl.closedon_date >= :dueDate)) "); if (checkForOfficeId) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java index 3d952bc80..86448f73a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java @@ -46,6 +46,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.SQLBuilder; @@ -94,7 +95,8 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService private final CenterDataMapper centerMapper = new CenterDataMapper(); private final GroupDataMapper groupDataMapper = new GroupDataMapper(); - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PaginationParametersDataValidator paginationParametersDataValidator; private static final Set SUPPORTED_ORDER_BY_VALUES = new HashSet<>(Arrays.asList("id", "name", "officeId", "officeName")); @@ -104,7 +106,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService final StaffReadPlatformService staffReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService, final PaginationParametersDataValidator paginationParametersDataValidator, final ConfigurationDomainService configurationDomainService, final CalendarReadPlatformService calendarReadPlatformService, - final ColumnValidator columnValidator) { + final ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.context = context; this.clientReadPlatformService = clientReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); @@ -115,6 +117,8 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService this.configurationDomainService = configurationDomainService; this.calendarReadPlatformService = calendarReadPlatformService; this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } // 'g.' preffix because of ERROR 1052 (23000): Column 'column_name' in where @@ -336,7 +340,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService final String hierarchySearchString = hierarchy + "%"; final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.centerMapper.schema()); final SQLBuilder extraCriteria = getCenterExtraCriteria(this.centerMapper.schema(), searchParameters); extraCriteria.addNonNullCriteria("o.hierarchy like ", hierarchySearchString); @@ -349,15 +353,15 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), extraCriteria.getArguments(), - this.centerMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), extraCriteria.getArguments(), this.centerMapper); } @Override @@ -383,9 +387,11 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupReadPlatformServiceImpl.java index c048ec372..b991fb0b5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupReadPlatformServiceImpl.java @@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.SQLBuilder; @@ -67,7 +68,8 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { private final CodeValueReadPlatformService codeValueReadPlatformService; private final AllGroupTypesDataMapper allGroupTypesDataMapper = new AllGroupTypesDataMapper(); - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PaginationParametersDataValidator paginationParametersDataValidator; private final ColumnValidator columnValidator; @@ -77,7 +79,8 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { public GroupReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, final CenterReadPlatformService centerReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService, - final PaginationParametersDataValidator paginationParametersDataValidator, final ColumnValidator columnValidator) { + final PaginationParametersDataValidator paginationParametersDataValidator, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.centerReadPlatformService = centerReadPlatformService; @@ -86,6 +89,8 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { this.codeValueReadPlatformService = codeValueReadPlatformService; this.paginationParametersDataValidator = paginationParametersDataValidator; this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -144,7 +149,7 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { final String hierarchySearchString = hierarchy + "%"; final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.allGroupTypesDataMapper.schema()); final SQLBuilder extraCriteria = getGroupExtraCriteria(this.allGroupTypesDataMapper.schema(), searchParameters); @@ -163,8 +168,7 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), extraCriteria.getArguments(), + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), extraCriteria.getArguments(), this.allGroupTypesDataMapper); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java index 84877b248..e2479f4da 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartReadPlatformServiceImpl.java @@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformSer import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.client.api.ClientApiConstants; @@ -54,21 +55,24 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha private final PlatformSecurityContext context; private final JdbcTemplate jdbcTemplate; private final InterestRateChartMapper chartRowMapper = new InterestRateChartMapper(); - private final InterestRateChartExtractor chartExtractor = new InterestRateChartExtractor(); + private final InterestRateChartExtractor chartExtractor; private final InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService; private final InterestIncentiveDropdownReadPlatformService interestIncentiveDropdownReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public InterestRateChartReadPlatformServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource, InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService, final InterestIncentiveDropdownReadPlatformService interestIncentiveDropdownReadPlatformService, - final CodeValueReadPlatformService codeValueReadPlatformService) { + final CodeValueReadPlatformService codeValueReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.chartDropdownReadPlatformService = chartDropdownReadPlatformService; this.interestIncentiveDropdownReadPlatformService = interestIncentiveDropdownReadPlatformService; this.codeValueReadPlatformService = codeValueReadPlatformService; + this.sqlGenerator = sqlGenerator; + chartExtractor = new InterestRateChartExtractor(this.sqlGenerator); } @Override @@ -180,7 +184,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha private static final class InterestRateChartExtractor implements ResultSetExtractor> { InterestRateChartMapper chartMapper = new InterestRateChartMapper(); - InterestRateChartSlabExtractor chartSlabsMapper = new InterestRateChartSlabExtractor(); + InterestRateChartSlabExtractor chartSlabsMapper; private final String schemaSql; @@ -188,7 +192,8 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha return this.schemaSql; } - private InterestRateChartExtractor() { + private InterestRateChartExtractor(DatabaseSpecificSQLGenerator sqlGenerator) { + chartSlabsMapper = new InterestRateChartSlabExtractor(sqlGenerator); final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,") @@ -206,7 +211,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha .append("from ") .append("m_interest_rate_chart irc left join m_interest_rate_slab ircd on irc.id=ircd.interest_rate_chart_id ") .append(" left join m_interest_incentives iri on iri.interest_rate_slab_id = ircd.id ") - .append(" left join m_code_value code on code.id = iri.attribute_value ") + .append(" left join m_code_value code on " + sqlGenerator.castChar("code.id") + " = iri.attribute_value ") .append("left join m_currency curr on ircd.currency_code= curr.code ") .append("left join m_deposit_product_interest_rate_chart dpirc on irc.id=dpirc.interest_rate_chart_id ") .append("left join m_savings_product sp on sp.id=dpirc.deposit_product_id "); @@ -289,7 +294,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha return this.schemaSql; } - private InterestRateChartSlabsMapper() { + private InterestRateChartSlabsMapper(DatabaseSpecificSQLGenerator sqlGenerator) { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ").append( @@ -302,7 +307,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha .append(" iri.incentive_type as incentiveType, iri.amount as amount, ").append("code.code_value as attributeValueDesc ") .append("from ").append("m_interest_rate_slab ircd ") .append(" left join m_interest_incentives iri on iri.interest_rate_slab_id = ircd.id ") - .append(" left join m_code_value code on code.id = iri.attribute_value ") + .append(" left join m_code_value code on " + sqlGenerator.castChar("code.id") + " = iri.attribute_value ") .append("left join m_currency curr on ircd.currency_code= curr.code "); this.schemaSql = sqlBuilder.toString(); } @@ -347,7 +352,7 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha private static final class InterestRateChartSlabExtractor implements ResultSetExtractor { - InterestRateChartSlabsMapper chartSlabsMapper = new InterestRateChartSlabsMapper(); + InterestRateChartSlabsMapper chartSlabsMapper; InterestIncentiveMapper incentiveMapper = new InterestIncentiveMapper(); private final String schemaSql; @@ -357,7 +362,8 @@ public class InterestRateChartReadPlatformServiceImpl implements InterestRateCha return this.schemaSql; } - private InterestRateChartSlabExtractor() { + private InterestRateChartSlabExtractor(DatabaseSpecificSQLGenerator sqlGenerator) { + chartSlabsMapper = new InterestRateChartSlabsMapper(sqlGenerator); this.schemaSql = chartSlabsMapper.schema(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java index aecc6799e..f1cfbab37 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/interestratechart/service/InterestRateChartSlabReadPlatformServiceImpl.java @@ -29,6 +29,7 @@ import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformSer import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.client.api.ClientApiConstants; @@ -49,21 +50,24 @@ public class InterestRateChartSlabReadPlatformServiceImpl implements InterestRat private final PlatformSecurityContext context; private final JdbcTemplate jdbcTemplate; - private final InterestRateChartSlabExtractor chartSlabExtractor = new InterestRateChartSlabExtractor(); + private final InterestRateChartSlabExtractor chartSlabExtractor; private final InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService; private final InterestIncentiveDropdownReadPlatformService interestIncentiveDropdownReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public InterestRateChartSlabReadPlatformServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource, InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService, final InterestIncentiveDropdownReadPlatformService interestIncentiveDropdownReadPlatformService, - final CodeValueReadPlatformService codeValueReadPlatformService) { + final CodeValueReadPlatformService codeValueReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.chartDropdownReadPlatformService = chartDropdownReadPlatformService; this.interestIncentiveDropdownReadPlatformService = interestIncentiveDropdownReadPlatformService; this.codeValueReadPlatformService = codeValueReadPlatformService; + this.sqlGenerator = sqlGenerator; + chartSlabExtractor = new InterestRateChartSlabExtractor(sqlGenerator); } @Override @@ -131,7 +135,7 @@ public class InterestRateChartSlabReadPlatformServiceImpl implements InterestRat return this.schemaSql; } - private InterestRateChartSlabsMapper() { + private InterestRateChartSlabsMapper(DatabaseSpecificSQLGenerator sqlGenerator) { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("ircd.id as ircdId, ircd.description as ircdDescription, ircd.period_type_enum ircdPeriodTypeId, ").append( @@ -144,7 +148,7 @@ public class InterestRateChartSlabReadPlatformServiceImpl implements InterestRat .append(" iri.incentive_type as incentiveType, iri.amount as amount, ").append("code.code_value as attributeValueDesc ") .append("from ").append("m_interest_rate_slab ircd ") .append(" left join m_interest_incentives iri on iri.interest_rate_slab_id = ircd.id ") - .append(" left join m_code_value code on code.id = iri.attribute_value ") + .append(" left join m_code_value code on " + sqlGenerator.castChar("code.id") + " = iri.attribute_value ") .append("left join m_currency curr on ircd.currency_code= curr.code "); this.schemaSql = sqlBuilder.toString(); } @@ -186,7 +190,7 @@ public class InterestRateChartSlabReadPlatformServiceImpl implements InterestRat private static final class InterestRateChartSlabExtractor implements ResultSetExtractor> { - InterestRateChartSlabsMapper chartSlabsMapper = new InterestRateChartSlabsMapper(); + InterestRateChartSlabsMapper chartSlabsMapper; InterestIncentiveMapper incentiveMapper = new InterestIncentiveMapper(); private final String schemaSql; @@ -195,7 +199,8 @@ public class InterestRateChartSlabReadPlatformServiceImpl implements InterestRat return this.schemaSql; } - private InterestRateChartSlabExtractor() { + private InterestRateChartSlabExtractor(DatabaseSpecificSQLGenerator sqlGenerator) { + chartSlabsMapper = new InterestRateChartSlabsMapper(sqlGenerator); this.schemaSql = chartSlabsMapper.schema(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 0cfe9b566..864536a8f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -182,7 +182,7 @@ public class Loan extends AbstractPersistableCustom { @Column(name = "loan_type_enum", nullable = false) private Integer loanType; - @ManyToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id", nullable = false) private LoanProduct loanProduct; @@ -323,7 +323,7 @@ public class Loan extends AbstractPersistableCustom { private Set loanOfficerHistory; @OrderBy(value = "installmentNumber") - @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.EAGER) + @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY) private List repaymentScheduleInstallments = new ArrayList<>(); @OrderBy(value = "dateOf, id") @@ -362,7 +362,7 @@ public class Loan extends AbstractPersistableCustom { @OrderBy(value = "expectedDisbursementDate, id") private List disbursementDetails = new ArrayList<>(); - @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.EAGER) + @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY) private List postDatedChecks = new ArrayList<>(); @OrderBy(value = "termApplicableFrom, id") @@ -372,7 +372,7 @@ public class Loan extends AbstractPersistableCustom { @Column(name = "total_recovered_derived", scale = 6, precision = 19) private BigDecimal totalRecovered; - @OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch = FetchType.EAGER) + @OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch = FetchType.LAZY) private LoanInterestRecalculationDetails loanInterestRecalculationDetails; @Column(name = "is_npa", nullable = false) @@ -408,7 +408,7 @@ public class Loan extends AbstractPersistableCustom { @Column(name = "is_topup", nullable = false) private boolean isTopup = false; - @OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch = FetchType.EAGER) + @OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch = FetchType.LAZY) private LoanTopupDetails loanTopupDetails; @OneToMany(fetch = FetchType.LAZY) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java index 85ade2880..fa2e85160 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java @@ -57,8 +57,10 @@ import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundEx import org.apache.fineract.portfolio.loanproduct.serialization.LoanProductDataValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleCalculationPlatformService { private final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer; @@ -205,6 +207,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC } @Override + @Transactional(readOnly = true) public LoanScheduleData generateLoanScheduleForVariableInstallmentRequest(Long loanId, final String json) { final Loan loan = this.loanAssembler.assembleFrom(loanId); this.loanScheduleAssembler.assempleVariableScheduleFrom(loan, json); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java index 3aaa9a63a..b613effc7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java @@ -43,8 +43,10 @@ import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional(readOnly = true) public class LoanScheduleHistoryReadPlatformServiceImpl implements LoanScheduleHistoryReadPlatformService { private final JdbcTemplate jdbcTemplate; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryWritePlatformServiceImpl.java index e36cddd92..1ad3031c7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryWritePlatformServiceImpl.java @@ -32,8 +32,10 @@ import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanResch import org.apache.fineract.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional public class LoanScheduleHistoryWritePlatformServiceImpl implements LoanScheduleHistoryWritePlatformService { private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java index 2ce48db1e..21a67de0f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java @@ -37,8 +37,10 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService; import org.apache.fineract.useradministration.domain.AppUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional public class LoanScheduleWritePlatformServiceImpl implements LoanScheduleWritePlatformService { private final LoanAccountDomainService loanAccountDomainService; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java index dd3140a38..8a5bc4430 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java @@ -34,6 +34,7 @@ import javax.sql.DataSource; import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; @@ -63,6 +64,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat private final LoanChargeReadPlatformService loanChargeReadPlatformService; private final JdbcTemplate jdbcTemplate; private final DataSource dataSource; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final AppUserRepositoryWrapper userRepository; private final LoanRepositoryWrapper loanRepositoryWrapper; @@ -72,9 +74,11 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat public LoanAccrualWritePlatformServiceImpl(final RoutingDataSource dataSource, final LoanReadPlatformService loanReadPlatformService, final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService, final AppUserRepositoryWrapper userRepository, - final LoanRepositoryWrapper loanRepositoryWrapper, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository) { + final LoanRepositoryWrapper loanRepositoryWrapper, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, + DatabaseSpecificSQLGenerator sqlGenerator) { this.loanReadPlatformService = loanReadPlatformService; this.dataSource = dataSource; + this.sqlGenerator = sqlGenerator; this.jdbcTemplate = new JdbcTemplate(this.dataSource); this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.loanChargeReadPlatformService = loanChargeReadPlatformService; @@ -274,7 +278,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat LoanTransactionType.ACCRUAL.getValue(), Date.from(accruedTill.atStartOfDay(ZoneId.systemDefault()).toInstant()), amount, interestportion, feeportion, penaltyportion, DateUtils.getDateOfTenant()); @SuppressWarnings("deprecation") - final Long transactonId = this.jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Long.class); + final Long transactonId = this.jdbcTemplate.queryForObject("SELECT " + sqlGenerator.lastInsertId(), Long.class); Map applicableCharges = scheduleAccrualData.getApplicableCharges(); String chargespaidSql = "INSERT INTO m_loan_charge_paid_by (loan_transaction_id, loan_charge_id, amount,installment_number) VALUES (?,?,?,?)"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java index 5c25c21e9..189b0699f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java @@ -32,6 +32,7 @@ import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEntity; @@ -58,14 +59,16 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService, Bus private static final Logger LOG = LoggerFactory.getLogger(LoanArrearsAgingServiceImpl.class); private final BusinessEventNotifierService businessEventNotifierService; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private final JdbcTemplate jdbcTemplate; @Autowired - public LoanArrearsAgingServiceImpl(final RoutingDataSource dataSource, - final BusinessEventNotifierService businessEventNotifierService) { + public LoanArrearsAgingServiceImpl(final RoutingDataSource dataSource, final BusinessEventNotifierService businessEventNotifierService, + DatabaseSpecificSQLGenerator sqlGenerator) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.businessEventNotifierService = businessEventNotifierService; + this.sqlGenerator = sqlGenerator; } @PostConstruct @@ -114,7 +117,8 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService, Bus updateSqlBuilder.append(" left join m_product_loan_recalculation_details prd on prd.product_id = ml.product_id "); updateSqlBuilder.append(" WHERE ml.loan_status_id = 300 "); // active updateSqlBuilder.append(" and mr.completed_derived is false "); - updateSqlBuilder.append(" and mr.duedate < SUBDATE(CURDATE(),INTERVAL COALESCE(ml.grace_on_arrears_ageing, 0) day) "); + updateSqlBuilder.append(" and mr.duedate < ") + .append(sqlGenerator.subDate(sqlGenerator.currentDate(), "COALESCE(ml.grace_on_arrears_ageing, 0)", "day")).append(" "); updateSqlBuilder.append(" and (prd.arrears_based_on_original_schedule = false or prd.arrears_based_on_original_schedule is null) "); updateSqlBuilder.append(" GROUP BY ml.id"); @@ -134,7 +138,7 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService, Bus int count = this.jdbcTemplate.queryForObject("select count(mla.loan_id) from m_loan_arrears_aging mla where mla.loan_id =?", Integer.class, loan.getId()); List updateStatement = new ArrayList<>(); - OriginalScheduleExtractor originalScheduleExtractor = new OriginalScheduleExtractor(loan.getId().toString()); + OriginalScheduleExtractor originalScheduleExtractor = new OriginalScheduleExtractor(loan.getId().toString(), sqlGenerator); Map> scheduleDate = this.jdbcTemplate.query(originalScheduleExtractor.schema, originalScheduleExtractor); if (scheduleDate.size() > 0) { @@ -204,13 +208,14 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService, Bus loanIdentifier.append("INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id "); loanIdentifier.append( "inner join m_product_loan_recalculation_details prd on prd.product_id = ml.product_id and prd.arrears_based_on_original_schedule = true "); - loanIdentifier.append( - "WHERE ml.loan_status_id = 300 and mr.completed_derived is false and mr.duedate < SUBDATE(CURDATE(),INTERVAL COALESCE(ml.grace_on_arrears_ageing, 0) day) group by ml.id"); + loanIdentifier.append("WHERE ml.loan_status_id = 300 and mr.completed_derived is false and mr.duedate < ") + .append(sqlGenerator.subDate(sqlGenerator.currentDate(), "COALESCE(ml.grace_on_arrears_ageing, 0)", "day")) + .append(" group by ml.id"); List loanIds = this.jdbcTemplate.queryForList(loanIdentifier.toString(), Long.class); if (!loanIds.isEmpty()) { String loanIdsAsString = loanIds.toString(); loanIdsAsString = loanIdsAsString.substring(1, loanIdsAsString.length() - 1); - OriginalScheduleExtractor originalScheduleExtractor = new OriginalScheduleExtractor(loanIdsAsString); + OriginalScheduleExtractor originalScheduleExtractor = new OriginalScheduleExtractor(loanIdsAsString, sqlGenerator); Map> scheduleDate = this.jdbcTemplate.query(originalScheduleExtractor.schema, originalScheduleExtractor); @@ -419,13 +424,14 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService, Bus private final String schema; - OriginalScheduleExtractor(final String loanIdsAsString) { + OriginalScheduleExtractor(final String loanIdsAsString, DatabaseSpecificSQLGenerator sqlGenerator) { final StringBuilder scheduleDetail = new StringBuilder(); scheduleDetail.append("select ml.id as loanId, mr.duedate as dueDate, mr.principal_amount as principalAmount, "); scheduleDetail.append( "mr.interest_amount as interestAmount, mr.fee_charges_amount as feeAmount, mr.penalty_charges_amount as penaltyAmount "); scheduleDetail.append("from m_loan ml INNER JOIN m_loan_repayment_schedule_history mr on mr.loan_id = ml.id "); - scheduleDetail.append("where mr.duedate < SUBDATE(CURDATE(),INTERVAL COALESCE(ml.grace_on_arrears_ageing, 0) day) and "); + scheduleDetail.append("where mr.duedate < " + + sqlGenerator.subDate(sqlGenerator.currentDate(), "COALESCE(ml.grace_on_arrears_ageing, 0)", "day") + " and "); scheduleDetail.append("ml.id IN(").append(loanIdsAsString).append(") and mr.version = ("); scheduleDetail.append("select max(lrs.version) from m_loan_repayment_schedule_history lrs where mr.loan_id = lrs.loan_id"); scheduleDetail.append(") order by ml.id,mr.duedate"); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 5872fb20f..029effa8a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator; @@ -129,9 +130,11 @@ import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; @Service +@Transactional(readOnly = true) public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private final JdbcTemplate jdbcTemplate; @@ -147,8 +150,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private final CodeValueReadPlatformService codeValueReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService; private final StaffReadPlatformService staffReadPlatformService; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); - private final LoanMapper loaanLoanMapper = new LoanMapper(); + private final PaginationHelper paginationHelper; + private final LoanMapper loaanLoanMapper; private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private final PaymentTypeReadPlatformService paymentTypeReadPlatformService; @@ -158,6 +161,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private final ConfigurationDomainService configurationDomainService; private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; private final ColumnValidator columnValidator; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, @@ -172,7 +176,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService, final ConfigurationDomainService configurationDomainService, final AccountDetailsReadPlatformService accountDetailsReadPlatformService, final LoanRepositoryWrapper loanRepositoryWrapper, - final ColumnValidator columnValidator) { + final ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.context = context; this.loanRepositoryWrapper = loanRepositoryWrapper; this.applicationCurrencyRepository = applicationCurrencyRepository; @@ -194,6 +198,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { this.configurationDomainService = configurationDomainService; this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; this.columnValidator = columnValidator; + this.loaanLoanMapper = new LoanMapper(sqlGenerator); + this.sqlGenerator = sqlGenerator; + this.paginationHelper = paginationHelper; } @Override @@ -204,7 +211,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String hierarchy = currentUser.getOffice().getHierarchy(); final String hierarchySearchString = hierarchy + "%"; - final LoanMapper rm = new LoanMapper(); + final LoanMapper rm = new LoanMapper(sqlGenerator); final StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder.append("select "); @@ -225,7 +232,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { // final AppUser currentUser = this.context.authenticatedUser(); this.context.authenticatedUser(); - final LoanMapper rm = new LoanMapper(); + final LoanMapper rm = new LoanMapper(sqlGenerator); String sql = "select " + rm.loanSchema() + " where l.account_no=?"; @@ -238,7 +245,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { // final AppUser currentUser = this.context.authenticatedUser(); this.context.authenticatedUser(); - final LoanMapper rm = new LoanMapper(); + final LoanMapper rm = new LoanMapper(sqlGenerator); String sql = "select " + rm.loanSchema() + " left join glim_parent_child_mapping as glim on glim.glim_child_account_id=l.account_no " @@ -271,7 +278,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { try { this.context.authenticatedUser(); - final LoanTransactionsMapper rm = new LoanTransactionsMapper(); + final LoanTransactionsMapper rm = new LoanTransactionsMapper(sqlGenerator); // retrieve all loan transactions that are not invalid and have not // been 'contra'ed by another transaction @@ -296,7 +303,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final String hierarchySearchString = hierarchy + "%"; final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.loaanLoanMapper.loanSchema()); // TODO - for time being this will data scope list of loans returned to @@ -351,17 +358,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } } final Object[] objectArray = extraCriterias.toArray(); final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, - this.loaanLoanMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.loaanLoanMapper); } @Override @@ -432,7 +439,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { this.context.authenticatedUser(); - RepaymentTransactionTemplateMapper mapper = new RepaymentTransactionTemplateMapper(); + RepaymentTransactionTemplateMapper mapper = new RepaymentTransactionTemplateMapper(sqlGenerator); String sql = "select " + mapper.schema(); LoanTransactionData loanTransactionData = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { LoanTransactionType.REPAYMENT.getValue(), loanId, loanId }); @@ -574,7 +581,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { public LoanTransactionData retrieveLoanTransaction(final Long loanId, final Long transactionId) { this.context.authenticatedUser(); try { - final LoanTransactionsMapper rm = new LoanTransactionsMapper(); + final LoanTransactionsMapper rm = new LoanTransactionsMapper(sqlGenerator); final String sql = "select " + rm.loanPaymentsSchema() + " where l.id = ? and tr.id = ? "; return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId, transactionId }); } catch (final EmptyResultDataAccessException e) { @@ -584,6 +591,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private static final class LoanMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + LoanMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String loanSchema() { return "l.id as id, l.account_no as accountNo, l.external_id as externalId, l.fund_id as fundId, f.name as fundName," + " l.loan_type_enum as loanType, l.loanpurpose_cv_id as loanPurposeId, cv.code_value as loanPurposeName," @@ -614,7 +627,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion," + " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, " + " lps.name as transactionStrategyName, " - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc." + + sqlGenerator.escape("name") + + " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName, " + " l.principal_disbursed_derived as principalDisbursed," + " l.principal_repaid_derived as principalPaid," + " l.principal_writtenoff_derived as principalWrittenOff," @@ -664,8 +679,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.total_recovered_derived as totalRecovered" + ", topuploan.account_no as closureLoanAccountNo, " + " topup.topup_amount as topupAmount " + " from m_loan l" // + " join m_product_loan lp on lp.id = l.product_id" // - + " left join m_loan_recalculation_details lir on lir.loan_id = l.id " - + " join m_currency rc on rc.`code` = l.currency_code" // + + " left join m_loan_recalculation_details lir on lir.loan_id = l.id " + " join m_currency rc on rc." + + sqlGenerator.escape("code") + " = l.currency_code" // + " left join m_client c on c.id = l.client_id" // + " left join m_group g on g.id = l.group_id" // + " left join m_loan_arrears_aging la on la.loan_id = l.id" // @@ -1286,17 +1301,24 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private static final class LoanTransactionsMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + LoanTransactionsMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String loanPaymentsSchema() { - return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, " - + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, " + return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as " + sqlGenerator.escape("date") + + ", tr.amount as total, " + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, " + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, " + " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, " + " tr.unrecognized_income_portion as unrecognizedIncome," + " tr.submitted_on_date as submittedOnDate, " + " tr.manually_adjusted_or_reversed as manuallyReversed, " + " pd.payment_type_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, " + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, l.net_disbursal_amount as netDisbursalAmount," - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc." + + sqlGenerator.escape("name") + " as currencyName, " + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + " pt.value as paymentTypeName, tr.external_id as externalId, tr.office_id as officeId, office.name as officeName, " + " fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed," @@ -1305,7 +1327,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " totran.id as toTransferId, totran.is_reversed as toTransferReversed," + " totran.transaction_date as toTransferDate, totran.amount as toTransferAmount," + " totran.description as toTransferDescription " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id" - + " join m_currency rc on rc.`code` = l.currency_code " + + " join m_currency rc on rc." + sqlGenerator.escape("code") + " = l.currency_code " + " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id" + " left join m_payment_type pt on pd.payment_type_id = pt.id" + " left join m_office office on office.id=tr.office_id" + " left join m_account_transfer_transaction fromtran on fromtran.from_loan_transaction_id = tr.id " @@ -1529,7 +1551,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final MusoniOverdueLoanScheduleMapper rm = new MusoniOverdueLoanScheduleMapper(); final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("select ").append(rm.schema()).append(" where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate ") + sqlBuilder.append("select ").append(rm.schema()) + .append(" where " + sqlGenerator.subDate(sqlGenerator.currentDate(), "?", "day") + " > ls.duedate ") .append(" and ls.completed_derived <> true and mc.charge_applies_to_enum =1 ") .append(" and ls.recalculated_interest_component <> true ") .append(" and mc.charge_time_enum = 9 and ml.loan_status_id = 300 "); @@ -1539,7 +1562,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } // Only apply for duedate = yesterday (so that we don't apply // penalties on the duedate itself) - sqlBuilder.append(" and ls.duedate >= DATE_SUB(CURDATE(),INTERVAL (? + 1) DAY)"); + sqlBuilder.append(" and ls.duedate >= " + sqlGenerator.subDate(sqlGenerator.currentDate(), "(? + 1)", "day")); return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod, penaltyWaitPeriod }); } @@ -1560,7 +1583,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { @Override public Collection retrieveLoanDisbursementDetails(final Long loanId) { - final LoanDisbursementDetailMapper rm = new LoanDisbursementDetailMapper(); + final LoanDisbursementDetailMapper rm = new LoanDisbursementDetailMapper(sqlGenerator); final String sql = "select " + rm.schema() + " where dd.loan_id=? group by dd.id, lc.amount_waived_derived order by dd.expected_disburse_date"; return this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); @@ -1568,8 +1591,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private static final class LoanDisbursementDetailMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + LoanDisbursementDetailMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String schema() { - return "dd.id as id,dd.expected_disburse_date as expectedDisbursementdate, dd.disbursedon_date as actualDisbursementdate,dd.principal as principal,dd.net_disbursal_amount as netDisbursalAmount,sum(lc.amount) chargeAmount, lc.amount_waived_derived waivedAmount,group_concat(lc.id) loanChargeId " + return "dd.id as id,dd.expected_disburse_date as expectedDisbursementdate, dd.disbursedon_date as actualDisbursementdate,dd.principal as principal,dd.net_disbursal_amount as netDisbursalAmount,sum(lc.amount) chargeAmount, lc.amount_waived_derived waivedAmount, " + + sqlGenerator.groupConcat("lc.id") + " loanChargeId " + "from m_loan l inner join m_loan_disbursement_detail dd on dd.loan_id = l.id left join m_loan_tranche_disbursement_charge tdc on tdc.disbursement_detail_id=dd.id " + "left join m_loan_charge lc on lc.id=tdc.loan_charge_id and lc.is_active=true"; } @@ -1596,7 +1626,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { @Override public DisbursementData retrieveLoanDisbursementDetail(Long loanId, Long disbursementId) { - final LoanDisbursementDetailMapper rm = new LoanDisbursementDetailMapper(); + final LoanDisbursementDetailMapper rm = new LoanDisbursementDetailMapper(sqlGenerator); final String sql = "select " + rm.schema() + " where dd.loan_id=? and dd.id=? group by dd.id, lc.amount_waived_derived"; return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId, disbursementId }); } @@ -1642,7 +1672,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { .append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))") .append(" or ( ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))") .append(" or ( ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))") - .append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and loan.is_npa=false and ls.duedate <= CURDATE()) "); + .append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and loan.is_npa=false and ls.duedate <= " + + sqlGenerator.currentDate() + ") "); if (organisationStartDate != null) { sqlBuilder.append(" and ls.duedate > :organisationstartdate "); } @@ -1662,27 +1693,26 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper(); Date organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate(); + String formattedTillDate = formatter.format(tillDate); final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("select ").append(mapper.schema()).append( " where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ") .append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))") .append(" or (ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))") .append(" or (ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))") - .append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and (loan.closedon_date <= :tilldate" - + " or loan.closedon_date is null)") - .append(" and loan.is_npa=false and (ls.duedate <= :tilldate or (ls.duedate > :tilldate" - + " and ls.fromdate < :tilldate))) "); + .append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and (loan.closedon_date <= '" + formattedTillDate + + "' or loan.closedon_date is null)") + .append(" and loan.is_npa=false and (ls.duedate <= '" + formattedTillDate + "' or (ls.duedate > '" + formattedTillDate + + "' and ls.fromdate < '" + formattedTillDate + "'))) "); if (organisationStartDate != null) { - sqlBuilder.append(" and ls.duedate > :organisationstartdate "); + String formattedOrganizationStartDate = formatter + .format(LocalDate.ofInstant(organisationStartDate.toInstant(), DateUtils.getDateTimeZoneOfTenant())); + sqlBuilder.append(" and ls.duedate > '" + formattedOrganizationStartDate + "' "); } sqlBuilder.append(" order by loan.id,ls.duedate "); Map paramMap = new HashMap<>(4); paramMap.put("active", LoanStatus.ACTIVE.getValue()); paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue()); - paramMap.put("tilldate", formatter.format(tillDate)); - paramMap.put("organisationstartdate", - (organisationStartDate == null) ? formatter.format(LocalDate.now(DateUtils.getDateTimeZoneOfTenant())) - : formatter.format(LocalDate.ofInstant(organisationStartDate.toInstant(), DateUtils.getDateTimeZoneOfTenant()))); return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper); } @@ -1879,6 +1909,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { @Override public List fetchLoansForInterestRecalculation(Integer pageSize, Long maxLoanIdInList, String officeHierarchy) { + String currentdate = formatter.format(DateUtils.getLocalDateOfTenant()); + // will look only for yesterday modified rates + String yesterday = formatter.format(DateUtils.getLocalDateOfTenant().minusDays(1)); StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder.append("SELECT ml.id FROM m_loan ml "); sqlBuilder.append(" left join m_client mc on mc.id = ml.client_id "); @@ -1893,19 +1926,20 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { sqlBuilder.append(" left join m_loan_reschedule_request lrr on lrr.loan_id = ml.id"); // this is to identify the applicable rates when base rate is changed sqlBuilder.append(" left join m_floating_rates bfr on bfr.is_base_lending_rate = true"); - sqlBuilder.append(" left join m_floating_rates_periods bfrp on bfr.id = bfrp.floating_rates_id and bfrp.created_date >= ?"); + sqlBuilder.append(" left join m_floating_rates_periods bfrp on bfr.id = bfrp.floating_rates_id and bfrp.created_date >= '" + + yesterday + "'"); sqlBuilder.append(" WHERE ml.loan_status_id = ? "); sqlBuilder.append(" and ml.is_npa = false "); sqlBuilder.append(" and (("); sqlBuilder.append("ml.interest_recalculation_enabled = true "); - sqlBuilder.append(" and (ml.interest_recalcualated_on is null or ml.interest_recalcualated_on <> ?)"); + sqlBuilder.append(" and (ml.interest_recalcualated_on is null or ml.interest_recalcualated_on <> '" + currentdate + "')"); sqlBuilder.append(" and (("); sqlBuilder.append(" mr.completed_derived is false "); - sqlBuilder.append(" and mr.duedate < ? )"); - sqlBuilder.append(" or dd.expected_disburse_date < ? )) "); + sqlBuilder.append(" and mr.duedate < '" + currentdate + "' )"); + sqlBuilder.append(" or dd.expected_disburse_date < '" + currentdate + "' )) "); sqlBuilder.append(" or ("); sqlBuilder.append(" fr.is_active = true and frp.is_active = true"); - sqlBuilder.append(" and (frp.created_date >= ? or "); + sqlBuilder.append(" and (frp.created_date >= '" + yesterday + "' or "); sqlBuilder .append("(bfrp.id is not null and frp.is_differential_to_base_lending_rate = true and frp.from_date >= bfrp.from_date)) "); sqlBuilder.append("and lrr.loan_id is null"); @@ -1914,12 +1948,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { sqlBuilder.append(" group by ml.id "); sqlBuilder.append(" limit ? "); try { - String currentdate = formatter.format(DateUtils.getLocalDateOfTenant()); - // will look only for yesterday modified rates - String yesterday = formatter.format(DateUtils.getLocalDateOfTenant().minusDays(1)); return Collections.synchronizedList(this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class, - new Object[] { yesterday, LoanStatus.ACTIVE.getValue(), currentdate, currentdate, currentdate, yesterday, - maxLoanIdInList, officeHierarchy, pageSize })); + new Object[] { LoanStatus.ACTIVE.getValue(), maxLoanIdInList, officeHierarchy, pageSize })); } catch (final EmptyResultDataAccessException e) { return null; } @@ -1929,7 +1959,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { public Collection retrieveWaiverLoanTransactions(final Long loanId) { try { - final LoanTransactionDerivedComponentMapper rm = new LoanTransactionDerivedComponentMapper(); + final LoanTransactionDerivedComponentMapper rm = new LoanTransactionDerivedComponentMapper(sqlGenerator); final String sql = "select " + rm.schema() + " where tr.loan_id = ? and tr.transaction_type_enum = ? and tr.is_reversed=false order by tr.transaction_date ASC,id "; @@ -1947,10 +1977,16 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private static final class LoanTransactionDerivedComponentMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + LoanTransactionDerivedComponentMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String schema() { - return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, " - + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, " + return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as " + sqlGenerator.escape("date") + + ", tr.amount as total, " + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, " + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, " + " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, " + " tr.unrecognized_income_portion as unrecognizedIncome " + " from m_loan_transaction tr "; @@ -2076,7 +2112,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " + SUM(COALESCE(mr.interest_completed_derived, 0)) " + " + SUM(COALESCE(mr.fee_charges_completed_derived, 0)) " + " + SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived " + " from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id " - + " where ml.id=? and mr.duedate >= CURDATE() group by ml.id having " + + " where ml.id=? and mr.duedate >= " + sqlGenerator.currentDate() + " group by ml.id having " + " (SUM(COALESCE(mr.principal_completed_derived, 0)) " + " + SUM(COALESCE(mr.interest_completed_derived, 0)) " + " + SUM(COALESCE(mr.fee_charges_completed_derived, 0)) " + "+ SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0"; @@ -2158,6 +2194,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { @Override public Collection retrieveLoanIdsWithPendingIncomePostingTransactions() { + String currentdate = formatter.format(DateUtils.getLocalDateOfTenant()); StringBuilder sqlBuilder = new StringBuilder().append(" select distinct loan.id ").append(" from m_loan as loan ").append( " inner join m_loan_recalculation_details as recdet on (recdet.loan_id = loan.id and recdet.is_compounding_to_be_posted_as_transaction is not null and recdet.is_compounding_to_be_posted_as_transaction = true) ") .append(" inner join m_loan_repayment_schedule as repsch on repsch.loan_id = loan.id ") @@ -2165,10 +2202,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { .append(" left join m_loan_transaction as trans on (trans.is_reversed <> true and trans.transaction_type_enum = 19 and trans.loan_id = loan.id and trans.transaction_date = adddet.effective_date) ") .append(" where loan.loan_status_id = 300 ").append(" and loan.is_npa = false ") .append(" and adddet.effective_date is not null ").append(" and trans.transaction_date is null ") - .append(" and adddet.effective_date < ? "); + .append(" and adddet.effective_date < '" + currentdate + "' "); try { - String currentdate = formatter.format(DateUtils.getLocalDateOfTenant()); - return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class, new Object[] { currentdate }); + return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class); } catch (final EmptyResultDataAccessException e) { return null; } @@ -2222,8 +2258,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { private static final class RepaymentTransactionTemplateMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; private CurrencyMapper currencyMapper = new CurrencyMapper(); + RepaymentTransactionTemplateMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String schema() { StringBuilder sqlBuilder = new StringBuilder(); @@ -2238,12 +2279,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { sqlBuilder.append( "ls.penalty_charges_amount - coalesce(ls.penalty_charges_completed_derived,0) - coalesce(ls.penalty_charges_writtenoff_derived,0) - coalesce(ls.penalty_charges_waived_derived,0) as penaltyDue, "); sqlBuilder.append( - "l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, l.net_disbursal_amount as netDisbursalAmount, rc.`name` as currencyName, "); + "l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, l.net_disbursal_amount as netDisbursalAmount, rc." + + sqlGenerator.escape("name") + " as currencyName, "); sqlBuilder.append("rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode "); sqlBuilder.append("FROM m_loan l "); sqlBuilder.append( "LEFT JOIN m_loan_transaction tr ON tr.loan_id = l.id AND tr.transaction_type_enum = ? and tr.is_reversed = false "); - sqlBuilder.append("join m_currency rc on rc.`code` = l.currency_code "); + sqlBuilder.append("join m_currency rc on rc." + sqlGenerator.escape("code") + " = l.currency_code "); sqlBuilder.append("JOIN m_loan_repayment_schedule ls ON ls.loan_id = l.id AND ls.completed_derived = 0 "); sqlBuilder.append("join( "); sqlBuilder.append("(select min(ls.duedate) datedue,ls.loan_id from m_loan_repayment_schedule ls "); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index 29a9b4766..f228311b1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -29,6 +29,7 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityType; import org.apache.fineract.infrastructure.entityaccess.service.FineractEntityAccessUtil; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; @@ -58,17 +59,20 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo private final JdbcTemplate jdbcTemplate; private final ChargeReadPlatformService chargeReadPlatformService; private final RateReadService rateReadService; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final FineractEntityAccessUtil fineractEntityAccessUtil; @Autowired public LoanProductReadPlatformServiceImpl(final PlatformSecurityContext context, final ChargeReadPlatformService chargeReadPlatformService, final RoutingDataSource dataSource, - final FineractEntityAccessUtil fineractEntityAccessUtil, final RateReadService rateReadService) { + final FineractEntityAccessUtil fineractEntityAccessUtil, final RateReadService rateReadService, + DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.chargeReadPlatformService = chargeReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); this.fineractEntityAccessUtil = fineractEntityAccessUtil; this.rateReadService = rateReadService; + this.sqlGenerator = sqlGenerator; } @Override @@ -121,7 +125,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo this.context.authenticatedUser(); - final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + final LoanProductLookupMapper rm = new LoanProductLookupMapper(sqlGenerator); String sql = "select " + rm.schema(); @@ -143,7 +147,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo public Collection retrieveAllLoanProductsForLookup(final boolean activeOnly) { this.context.authenticatedUser(); - final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + final LoanProductLookupMapper rm = new LoanProductLookupMapper(sqlGenerator); String sql = "select "; if (activeOnly) { @@ -479,12 +483,18 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo private static final class LoanProductLookupMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + LoanProductLookupMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + public String schema() { return "lp.id as id, lp.name as name, lp.allow_multiple_disbursals as multiDisburseLoan from m_product_loan lp"; } public String activeOnlySchema() { - return schema() + " where (close_date is null or close_date >= CURDATE())"; + return schema() + " where (close_date is null or close_date >= " + sqlGenerator.currentDate() + ")"; } public String productMixSchema() { @@ -563,7 +573,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo this.context.authenticatedUser(); - final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + final LoanProductLookupMapper rm = new LoanProductLookupMapper(sqlGenerator); String sql = "Select " + rm.productMixSchema(); @@ -583,7 +593,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo this.context.authenticatedUser(); - final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + final LoanProductLookupMapper rm = new LoanProductLookupMapper(sqlGenerator); String sql = "Select " + rm.restrictedProductsSchema() + " where pm.product_id=? "; // Check if branch specific products are enabled. If yes, fetch only @@ -612,7 +622,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo this.context.authenticatedUser(); - final LoanProductLookupMapper rm = new LoanProductLookupMapper(); + final LoanProductLookupMapper rm = new LoanProductLookupMapper(sqlGenerator); String sql = "Select " + rm.schema() + " where "; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/repaymentwithpostdatedchecks/service/RepaymentWithPostDatedChecksReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/repaymentwithpostdatedchecks/service/RepaymentWithPostDatedChecksReadPlatformServiceImpl.java index 59ec8543e..b3f160720 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/repaymentwithpostdatedchecks/service/RepaymentWithPostDatedChecksReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/repaymentwithpostdatedchecks/service/RepaymentWithPostDatedChecksReadPlatformServiceImpl.java @@ -32,8 +32,10 @@ import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDat import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.exception.PostDatedCheckNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional(readOnly = true) public class RepaymentWithPostDatedChecksReadPlatformServiceImpl implements RepaymentWithPostDatedChecksReadPlatformService { private final PostDatedChecksRepository postDatedChecksRepository; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java index 1ecf513bd..318f54af9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountInterestRateChartReadPlatformServiceImpl.java @@ -31,6 +31,7 @@ import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformSer import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.client.api.ClientApiConstants; @@ -59,21 +60,24 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D private final PlatformSecurityContext context; private final JdbcTemplate jdbcTemplate; private final DepositAccountInterestRateChartMapper chartRowMapper = new DepositAccountInterestRateChartMapper(); - private final DepositAccountInterestRateChartExtractor chartExtractor = new DepositAccountInterestRateChartExtractor(); + private final DepositAccountInterestRateChartExtractor chartExtractor; private final InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService; private final InterestIncentiveDropdownReadPlatformService interestIncentiveDropdownReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public DepositAccountInterestRateChartReadPlatformServiceImpl(PlatformSecurityContext context, final RoutingDataSource dataSource, InterestRateChartDropdownReadPlatformService chartDropdownReadPlatformService, final InterestIncentiveDropdownReadPlatformService interestIncentiveDropdownReadPlatformService, - final CodeValueReadPlatformService codeValueReadPlatformService) { + final CodeValueReadPlatformService codeValueReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.chartDropdownReadPlatformService = chartDropdownReadPlatformService; this.interestIncentiveDropdownReadPlatformService = interestIncentiveDropdownReadPlatformService; this.codeValueReadPlatformService = codeValueReadPlatformService; + this.sqlGenerator = sqlGenerator; + chartExtractor = new DepositAccountInterestRateChartExtractor(sqlGenerator); } @Override @@ -194,7 +198,7 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D return this.schemaSql; } - private DepositAccountInterestRateChartExtractor() { + private DepositAccountInterestRateChartExtractor(DatabaseSpecificSQLGenerator sqlGenerator) { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("irc.id as ircId, irc.name as ircName, irc.description as ircDescription,") @@ -212,7 +216,7 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D .append("from ") .append("m_savings_account_interest_rate_chart irc left join m_savings_account_interest_rate_slab ircd on irc.id=ircd.savings_account_interest_rate_chart_id ") .append(" left join m_savings_interest_incentives iri on iri.deposit_account_interest_rate_slab_id =ircd.id ") - .append(" left join m_code_value code on code.id = iri.attribute_value ") + .append(" left join m_code_value code on " + sqlGenerator.castChar("code.id") + " = iri.attribute_value ") .append("left join m_currency curr on ircd.currency_code= curr.code ") .append("left join m_savings_account sa on irc.savings_account_id=sa.id "); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java index 082d97bb6..dbdf63c43 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountOnHoldTransactionReadPlatformServiceImpl.java @@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.portfolio.savings.data.DepositAccountOnHoldTransactionData; import org.springframework.beans.factory.annotation.Autowired; @@ -41,16 +42,19 @@ import org.springframework.stereotype.Service; public class DepositAccountOnHoldTransactionReadPlatformServiceImpl implements DepositAccountOnHoldTransactionReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final ColumnValidator columnValidator; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; private final DepositAccountOnHoldTransactionsMapper mapper; @Autowired - public DepositAccountOnHoldTransactionReadPlatformServiceImpl(final RoutingDataSource dataSource, - final ColumnValidator columnValidator) { + public DepositAccountOnHoldTransactionReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); - mapper = new DepositAccountOnHoldTransactionsMapper(); + this.sqlGenerator = sqlGenerator; + this.mapper = new DepositAccountOnHoldTransactionsMapper(); this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; } @Override @@ -58,7 +62,7 @@ public class DepositAccountOnHoldTransactionReadPlatformServiceImpl implements D SearchParameters searchParameters) { final StringBuilder sqlBuilder = new StringBuilder(200); List paramObj = new ArrayList<>(2); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.mapper.schema()); sqlBuilder.append(" where tr.savings_account_id = ? "); @@ -79,15 +83,16 @@ public class DepositAccountOnHoldTransactionReadPlatformServiceImpl implements D } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; final Object[] finalObjectArray = paramObj.toArray(); - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, this.mapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.mapper); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java index 4018a7c07..4047bb8da 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java @@ -37,6 +37,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.staff.data.StaffData; @@ -107,7 +108,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead private final DepositAccountLookupMapper depositAccountLookupsRowMapper = new DepositAccountLookupMapper(); private final DepositAccountForMaturityMapper depositAccountForMaturityRowMapper = new DepositAccountForMaturityMapper(); private final PaginationParametersDataValidator paginationParametersDataValidator; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final DatabaseSpecificSQLGenerator sqlGenerator; + private final PaginationHelper paginationHelper; private final SavingsAccountTransactionsMapper transactionsMapper; private final ClientReadPlatformService clientReadPlatformService; private final GroupReadPlatformService groupReadPlatformService; @@ -137,11 +139,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead final InterestRateChartReadPlatformService productChartReadPlatformService, final SavingsAccountReadPlatformService savingsAccountReadPlatformService, final DropdownReadPlatformService dropdownReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService, - PaymentTypeReadPlatformService paymentTypeReadPlatformService) { + PaymentTypeReadPlatformService paymentTypeReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.accountChartReadPlatformService = chartReadPlatformService; this.paginationParametersDataValidator = paginationParametersDataValidator; + this.sqlGenerator = sqlGenerator; this.transactionsMapper = new SavingsAccountTransactionsMapper(); this.clientReadPlatformService = clientReadPlatformService; this.groupReadPlatformService = groupReadPlatformService; @@ -156,6 +160,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead this.dropdownReadPlatformService = dropdownReadPlatformService; this.calendarReadPlatformService = calendarReadPlatformService; this.paymentTypeReadPlatformService = paymentTypeReadPlatformService; + this.paginationHelper = paginationHelper; } @Override @@ -191,15 +196,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead } final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(depositAccountMapper.schema()); sqlBuilder.append(" where sa.deposit_type_enum = ? "); sqlBuilder.append(paginationParameters.paginationSql()); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), - new Object[] { depositAccountType.getValue() }, depositAccountMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), new Object[] { depositAccountType.getValue() }, + depositAccountMapper); } @Override @@ -222,11 +225,9 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead sqlBuilder.append(this.depositAccountForMaturityRowMapper.schema()); sqlBuilder.append(" WHERE da.deposit_type_enum in (?, ?) and da.status_enum = ?"); - LocalDate today = DateUtils.getLocalDateOfTenant(); - return this.jdbcTemplate.query(sqlBuilder.toString(), this.depositAccountForMaturityRowMapper, - new Object[] { formatter.format(today), DepositAccountType.FIXED_DEPOSIT.getValue(), - DepositAccountType.RECURRING_DEPOSIT.getValue(), SavingsAccountStatusType.ACTIVE.getValue() }); + new Object[] { DepositAccountType.FIXED_DEPOSIT.getValue(), DepositAccountType.RECURRING_DEPOSIT.getValue(), + SavingsAccountStatusType.ACTIVE.getValue() }); } @Override @@ -519,6 +520,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead @Override public Collection> retriveDataForRDScheduleCreation() { + String today = formatter.format(DateUtils.getLocalDateOfTenant()); final StringBuilder sb = new StringBuilder(300); sb.append(" select rd.savings_account_id savingsId, rd.mandatory_recommended_deposit_amount as amount,"); sb.append(" mc.recurrence as recurrence ,"); @@ -529,13 +531,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead sb.append(" inner join m_deposit_account_recurring_detail rd on rd.savings_account_id = dat.savings_account_id "); sb.append(" inner join m_calendar_instance mci on mci.entity_type_enum = ? and mci.entity_id = dat.savings_account_id "); sb.append(" inner join m_calendar mc on mc.id = mci.calendar_id and mc.calendar_type_enum = ?"); - sb.append(" inner join m_mandatory_savings_schedule ms on ms.savings_account_id = dat.savings_account_id and ms.duedate > ?"); + sb.append(" inner join m_mandatory_savings_schedule ms on ms.savings_account_id = dat.savings_account_id and ms.duedate > '" + today + + "'"); sb.append(" where dat.deposit_period is null"); sb.append(" group by ms.savings_account_id, rd.mandatory_recommended_deposit_amount, mc.recurrence, rd.savings_account_id"); return this.jdbcTemplate.queryForList(sb.toString(), SavingsAccountStatusType.ACTIVE.getValue(), - CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue(), - formatter.format(DateUtils.getLocalDateOfTenant())); + CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue()); } private abstract static class DepositAccountMapper implements RowMapper { @@ -1383,22 +1385,20 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead private static final class DepositAccountForMaturityMapper implements RowMapper { - private final String schemaSql; + private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - DepositAccountForMaturityMapper() { + public String schema() { + LocalDate today = DateUtils.getLocalDateOfTenant(); + String formattedToday = formatter.format(today); final StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append("da.id as id, "); sqlBuilder.append("da.account_no as accountNumber, "); sqlBuilder.append("da.deposit_type_enum as depositTypeId "); sqlBuilder.append("FROM m_savings_account da "); sqlBuilder.append("inner join m_deposit_account_term_and_preclosure dat on dat.savings_account_id = da.id "); - sqlBuilder.append("and dat.maturity_date is not null and dat.maturity_date <= ? "); + sqlBuilder.append("and dat.maturity_date is not null and dat.maturity_date <= '" + formattedToday + "' "); - this.schemaSql = sqlBuilder.toString(); - } - - public String schema() { - return this.schemaSql; + return sqlBuilder.toString(); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 14504c9f4..1e5fca901 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -40,6 +40,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum; @@ -101,6 +102,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead private final SavingsProductReadPlatformService savingsProductReadPlatformService; private final StaffReadPlatformService staffReadPlatformService; private final SavingsDropdownReadPlatformService dropdownReadPlatformService; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final ChargeReadPlatformService chargeReadPlatformService; private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @@ -113,7 +115,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead // private final SavingsAccountAnnualFeeMapper annualFeeMapper; // pagination - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; private final EntityDatatableChecksReadService entityDatatableChecksReadService; private final ColumnValidator columnValidator; @@ -126,7 +128,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService, final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator, - final SavingsAccountAssembler savingAccountAssembler) { + final SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper, + DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); this.clientReadPlatformService = clientReadPlatformService; @@ -134,6 +137,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead this.savingsProductReadPlatformService = savingProductReadPlatformService; this.staffReadPlatformService = staffReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService; + this.sqlGenerator = sqlGenerator; this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper(); this.transactionsMapper = new SavingsAccountTransactionsMapper(); this.savingsAccountTransactionsForBatchMapper = new SavingsAccountTransactionsForBatchMapper(); @@ -142,6 +146,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead this.chargeReadPlatformService = chargeReadPlatformService; this.entityDatatableChecksReadService = entityDatatableChecksReadService; this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; this.savingAccountMapperForInterestPosting = new SavingAccountMapperForInterestPosting(); this.savingAccountAssembler = savingAccountAssembler; } @@ -184,7 +189,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final String hierarchySearchString = hierarchy + "%"; final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.savingAccountMapper.schema()); sqlBuilder.append(" join m_office o on o.id = c.office_id"); @@ -222,16 +227,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } } final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos); - final String sqlCountRows = "SELECT FOUND_ROWS()"; - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, - this.savingAccountMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.savingAccountMapper); } @Override @@ -271,7 +276,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead + "where (CASE WHEN sa.interest_posted_till_date is not null THEN tr.transaction_date >= sa.interest_posted_till_date ELSE tr.transaction_date >= sa.activatedon_date END) "; } - sql = sql + "and apm.product_type=2 and sa.interest_posted_till_date<" + java.sql.Date.valueOf(currentDate); + sql = sql + "and apm.product_type=2 and sa.interest_posted_till_date < '" + java.sql.Date.valueOf(currentDate) + "'"; sql = sql + " order by sa.id, tr.transaction_date, tr.created_date, tr.id"; List savingsAccountDataList = this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting, @@ -1650,20 +1655,21 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead @Override public List retrieveSavingsIdsPendingInactive(LocalDate tenantLocalDate) { + String formattedTenantLocalDate = formatter.format(tenantLocalDate); List ret = null; StringBuilder sql = new StringBuilder("select sa.id "); sql.append(" from m_savings_account as sa "); - sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) "); + sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = true) "); sql.append(" where sa.status_enum = 300 "); sql.append(" and sa.sub_status_enum = 0 "); - sql.append(" and DATEDIFF(?,(select COALESCE(max(sat.transaction_date), sa.activatedon_date) "); - sql.append(" from m_savings_account_transaction as sat "); - sql.append(" where sat.is_reversed = false "); - sql.append(" and sat.transaction_type_enum in (1,2) "); - sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_inactive "); + String compareDate = "(select COALESCE(max(sat.transaction_date), sa.activatedon_date) " + + "from m_savings_account_transaction as sat where sat.is_reversed = false" + + " and sat.transaction_type_enum in (1,2) and sat.savings_account_id = sa.id)"; + sql.append(" and ").append(sqlGenerator.dateDiff("'" + formattedTenantLocalDate + "'", compareDate)) + .append(" >= sp.days_to_inactive "); try { - ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] { formatter.format(tenantLocalDate) }); + ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class); } catch (EmptyResultDataAccessException e) { // ignore empty result scenario } catch (DataAccessException e) { @@ -1675,20 +1681,20 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead @Override public List retrieveSavingsIdsPendingDormant(LocalDate tenantLocalDate) { + String currentDate = formatter.format(tenantLocalDate); List ret = null; StringBuilder sql = new StringBuilder("select sa.id "); sql.append(" from m_savings_account as sa "); - sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) "); + sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = true) "); sql.append(" where sa.status_enum = 300 "); sql.append(" and sa.sub_status_enum = 100 "); - sql.append(" and DATEDIFF(?,(select COALESCE(max(sat.transaction_date),sa.activatedon_date) "); - sql.append(" from m_savings_account_transaction as sat "); - sql.append(" where sat.is_reversed = false "); - sql.append(" and sat.transaction_type_enum in (1,2) "); - sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_dormancy "); + sql.append(" and " + sqlGenerator.dateDiff("'" + currentDate + "'", + "(select COALESCE(max(sat.transaction_date),sa.activatedon_date) from m_savings_account_transaction as sat where sat.is_reversed = false and sat.transaction_type_enum in (1,2) and sat.savings_account_id = sa.id)") + + " "); + sql.append(" >= sp.days_to_dormancy "); try { - ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] { formatter.format(tenantLocalDate) }); + ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class); } catch (EmptyResultDataAccessException e) { // ignore empty result scenario } catch (DataAccessException e) { @@ -1706,11 +1712,10 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = true) "); sql.append(" where sa.status_enum = 300 "); sql.append(" and sa.sub_status_enum = 200 "); - sql.append(" and DATEDIFF(?,(select COALESCE(max(sat.transaction_date),sa.activatedon_date) "); - sql.append(" from m_savings_account_transaction as sat "); - sql.append(" where sat.is_reversed = false "); - sql.append(" and sat.transaction_type_enum in (1,2) "); - sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_escheat "); + sql.append(" and " + sqlGenerator.dateDiff("?", + "(select COALESCE(max(sat.transaction_date),sa.activatedon_date) from m_savings_account_transaction as sat where sat.is_reversed = false and sat.transaction_type_enum in (1,2) and sat.savings_account_id = sa.id)") + + " "); + sql.append(" >= sp.days_to_escheat "); try { ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] { formatter.format(tenantLocalDate) }); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java index 3e0097e75..b78a39bfe 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java @@ -108,6 +108,10 @@ public class SavingsSchedularInterestPoster implements Callable { this.tenant = tenant; } + public void setConfigurationDomainService(ConfigurationDomainService configurationDomainService) { + this.configurationDomainService = configurationDomainService; + } + public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java index e8d1b30bf..6793ad69f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java @@ -183,6 +183,7 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService { poster.setJdbcTemplate(jdbcTemplate); poster.setBackdatedTxnsAllowedTill(backdatedTxnsAllowedTill); poster.setTransactionTemplate(transactionTemplate); + poster.setConfigurationDomainService(configurationDomainService); posters.add(poster); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformServiceImpl.java index 155fb2aa9..e11a665fb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformServiceImpl.java @@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; @@ -45,12 +46,16 @@ public class ShareAccountDividendReadPlatformServiceImpl implements ShareAccount private final JdbcTemplate jdbcTemplate; private final ColumnValidator columnValidator; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired - public ShareAccountDividendReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator) { + public ShareAccountDividendReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -71,7 +76,7 @@ public class ShareAccountDividendReadPlatformServiceImpl implements ShareAccount public Page retriveAll(final Long payoutDetailId, final SearchParameters searchParameters) { ShareAccountDividendMapper shareAccountDividendMapper = new ShareAccountDividendMapper(); final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(shareAccountDividendMapper.schema()); sqlBuilder.append(" where sadd.dividend_pay_out_id = ? "); List params = new ArrayList<>(2); @@ -92,16 +97,16 @@ public class ShareAccountDividendReadPlatformServiceImpl implements ShareAccount } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; Object[] paramsObj = params.toArray(); - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), paramsObj, - shareAccountDividendMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), paramsObj, shareAccountDividendMapper); } private static final class ShareAccountDividendMapper implements RowMapper { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java index 5b42f6a57..a82cb183a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java @@ -36,6 +36,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.accountdetails.data.ShareAccountSummaryData; import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants; @@ -82,7 +83,8 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat private final PurchasedSharesReadPlatformService purchasedSharesReadPlatformService; private final JdbcTemplate jdbcTemplate; private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - private final PaginationHelper shareAccountDataPaginationHelper = new PaginationHelper<>(); + private final PaginationHelper shareAccountDataPaginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public ShareAccountReadPlatformServiceImpl(final RoutingDataSource dataSource, final ApplicationContext applicationContext, @@ -91,7 +93,8 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat final SavingsAccountReadPlatformService savingsAccountReadPlatformService, final ClientReadPlatformService clientReadPlatformService, final ShareAccountChargeReadPlatformService shareAccountChargeReadPlatformService, - final PurchasedSharesReadPlatformService purchasedSharesReadPlatformService) { + final PurchasedSharesReadPlatformService purchasedSharesReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.applicationContext = applicationContext; this.chargeReadPlatformService = chargeReadPlatformService; @@ -100,7 +103,8 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat this.clientReadPlatformService = clientReadPlatformService; this.shareAccountChargeReadPlatformService = shareAccountChargeReadPlatformService; this.purchasedSharesReadPlatformService = purchasedSharesReadPlatformService; - + this.shareAccountDataPaginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -190,7 +194,7 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat final Collection purchasedShares = null; ShareAccountMapper mapper = new ShareAccountMapper(charges, purchasedShares); StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(mapper.schema()); sqlBuilder.append(" where sa.status_enum = ? "); if (limit != null) { @@ -200,10 +204,8 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat sqlBuilder.append(" offset ").append(offSet); } - final String sqlCountRows = "SELECT FOUND_ROWS()"; Object[] whereClauseItemsitems = new Object[] { ShareAccountStatusType.ACTIVE.getValue() }; - return this.shareAccountDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), - whereClauseItemsitems, mapper); + return this.shareAccountDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), whereClauseItemsitems, mapper); } @Override @@ -223,10 +225,10 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat params.add(id); params.add(ShareAccountStatusType.ACTIVE.getValue()); if (fetchInActiveAccounts) { + String formattedStartDate = formatter.format(startDate); sb.append(" and (sa.status_enum = ? or (sa.status_enum = ? "); - sb.append(" and sa.closed_date > ?)) "); + sb.append(" and sa.closed_date > '" + formattedStartDate + "')) "); params.add(ShareAccountStatusType.CLOSED.getValue()); - params.add(formatter.format(startDate)); } else { sb.append(" and sa.status_enum = ? "); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java index e0fe77601..26d8e0dd0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java @@ -31,6 +31,7 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations; @@ -46,12 +47,16 @@ public class ShareProductDividendReadPlatformServiceImpl implements ShareProduct private final JdbcTemplate jdbcTemplate; private final ColumnValidator columnValidator; - private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final PaginationHelper paginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired - public ShareProductDividendReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator) { + public ShareProductDividendReadPlatformServiceImpl(final RoutingDataSource dataSource, final ColumnValidator columnValidator, + DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.columnValidator = columnValidator; + this.paginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -59,7 +64,7 @@ public class ShareProductDividendReadPlatformServiceImpl implements ShareProduct final SearchParameters searchParameters) { ShareProductDividendMapper shareProductDividendMapper = new ShareProductDividendMapper(); final StringBuilder sqlBuilder = new StringBuilder(200); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(shareProductDividendMapper.schema()); sqlBuilder.append(" where sp.id = ? "); List params = new ArrayList<>(2); @@ -79,16 +84,16 @@ public class ShareProductDividendReadPlatformServiceImpl implements ShareProduct } if (searchParameters.isLimited()) { - sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + sqlBuilder.append(" "); if (searchParameters.isOffset()) { - sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset())); + } else { + sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit())); } } - final String sqlCountRows = "SELECT FOUND_ROWS()"; Object[] paramsObj = params.toArray(); - return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), paramsObj, - shareProductDividendMapper); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), paramsObj, shareProductDividendMapper); } private static final class ShareProductDividendMapper implements RowMapper { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java index b02780578..5fdf26172 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java @@ -37,6 +37,7 @@ import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService; import org.apache.fineract.portfolio.charge.data.ChargeData; @@ -62,20 +63,24 @@ public class ShareProductReadPlatformServiceImpl implements ProductReadPlatformS private final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService; private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService; private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService; - private final PaginationHelper shareProductDataPaginationHelper = new PaginationHelper<>(); + private final PaginationHelper shareProductDataPaginationHelper; + private final DatabaseSpecificSQLGenerator sqlGenerator; @Autowired public ShareProductReadPlatformServiceImpl(final RoutingDataSource dataSource, final CurrencyReadPlatformService currencyReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService, final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService, final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService, - final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService) { + final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService, DatabaseSpecificSQLGenerator sqlGenerator, + PaginationHelper paginationHelper) { this.jdbcTemplate = new JdbcTemplate(dataSource); this.currencyReadPlatformService = currencyReadPlatformService; this.chargeReadPlatformService = chargeReadPlatformService; this.shareProductDropdownReadPlatformService = shareProductDropdownReadPlatformService; this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService; this.accountMappingReadPlatformService = accountMappingReadPlatformService; + this.shareProductDataPaginationHelper = paginationHelper; + this.sqlGenerator = sqlGenerator; } @Override @@ -84,7 +89,7 @@ public class ShareProductReadPlatformServiceImpl implements ProductReadPlatformS final Collection charges = null; ShareProductRowMapper mapper = new ShareProductRowMapper(shareMarketCollection, charges); StringBuilder sqlBuilder = new StringBuilder(); - sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(mapper.schema()); if (limit != null) { sqlBuilder.append(" limit ").append(limit); @@ -93,10 +98,8 @@ public class ShareProductReadPlatformServiceImpl implements ProductReadPlatformS sqlBuilder.append(" offset ").append(offSet); } - final String sqlCountRows = "SELECT FOUND_ROWS()"; Object[] whereClauseItemsitems = new Object[] {}; - return this.shareProductDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), - whereClauseItemsitems, mapper); + return this.shareProductDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), whereClauseItemsitems, mapper); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java index 0ffec88cf..0279f9559 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java @@ -38,6 +38,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceFactory; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail; @@ -83,6 +84,7 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService private final JobRegisterService jobRegisterService; private final ScheduledJobDetailRepository scheduledJobDetailsRepository; private final FineractProperties fineractProperties; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseTypeResolver databaseTypeResolver; @Autowired @@ -95,7 +97,7 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService final ShareAccountSchedularService shareAccountSchedularService, final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper, @Lazy final JobRegisterService jobRegisterService, final ScheduledJobDetailRepository scheduledJobDetailsRepository, final FineractProperties fineractProperties, - DatabaseTypeResolver databaseTypeResolver) { + DatabaseSpecificSQLGenerator sqlGenerator, DatabaseTypeResolver databaseTypeResolver) { this.dataSourceServiceFactory = dataSourceServiceFactory; this.savingsAccountWritePlatformService = savingsAccountWritePlatformService; this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService; @@ -107,6 +109,7 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService this.jobRegisterService = jobRegisterService; this.scheduledJobDetailsRepository = scheduledJobDetailsRepository; this.fineractProperties = fineractProperties; + this.sqlGenerator = sqlGenerator; this.databaseTypeResolver = databaseTypeResolver; } @@ -219,7 +222,7 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService updateSqlBuilder.append(" FROM m_loan ml "); updateSqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id "); updateSqlBuilder.append(" WHERE ml.loan_status_id = 300 "); - updateSqlBuilder.append(" and mr.duedate >= CURDATE() "); + updateSqlBuilder.append(" and mr.duedate >= " + sqlGenerator.currentDate() + " "); updateSqlBuilder.append(" GROUP BY ml.id"); updateSqlBuilder .append(" HAVING (SUM(coalesce(mr.principal_completed_derived, 0)) + SUM(coalesce(mr.interest_completed_derived, 0)) +"); @@ -313,7 +316,8 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService fromPart = " (select loan.id " + " FROM m_loan_arrears_aging laa" + " INNER JOIN m_loan loan on laa.loan_id = loan.id " + " INNER JOIN m_product_loan mpl on mpl.id = loan.product_id AND mpl.overdue_days_for_npa is not null " + "WHERE loan.loan_status_id = 300 and " + "laa.overdue_since_date_derived < " - + "SUBDATE(CURDATE(),INTERVAL COALESCE(mpl.overdue_days_for_npa, 0) day)" + " group by loan.id) as sl "; + + sqlGenerator.subDate(sqlGenerator.currentDate(), "COALESCE(mpl.overdue_days_for_npa, 0)", "day") + + " group by loan.id) as sl "; wherePart = " where ml.id=sl.id "; updateSqlBuilder.append("UPDATE m_loan as ml "); if (databaseTypeResolver.isMySQL()) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java index ee30fc55d..b03af1fb4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java @@ -23,6 +23,7 @@ import java.sql.SQLException; import java.util.Collection; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.useradministration.data.PasswordValidationPolicyData; import org.apache.fineract.useradministration.exception.PasswordValidationPolicyNotFoundException; import org.springframework.beans.factory.annotation.Autowired; @@ -35,12 +36,14 @@ import org.springframework.stereotype.Service; public class PasswordValidationPolicyReadPlatformServiceImpl implements PasswordValidationPolicyReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PasswordValidationPolicyMapper passwordValidationPolicyMapper; @Autowired - public PasswordValidationPolicyReadPlatformServiceImpl(final RoutingDataSource dataSource) { + public PasswordValidationPolicyReadPlatformServiceImpl(final RoutingDataSource dataSource, DatabaseSpecificSQLGenerator sqlGenerator) { this.jdbcTemplate = new JdbcTemplate(dataSource); - this.passwordValidationPolicyMapper = new PasswordValidationPolicyMapper(); + this.sqlGenerator = sqlGenerator; + this.passwordValidationPolicyMapper = new PasswordValidationPolicyMapper(sqlGenerator); } @Override @@ -62,6 +65,12 @@ public class PasswordValidationPolicyReadPlatformServiceImpl implements Password protected static final class PasswordValidationPolicyMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + public PasswordValidationPolicyMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + @Override public PasswordValidationPolicyData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { @@ -74,8 +83,8 @@ public class PasswordValidationPolicyReadPlatformServiceImpl implements Password } public String schema() { - return " pvp.id as id, pvp.active as active, pvp.description as description, pvp.`key` as `key`" - + " from m_password_validation_policy pvp"; + return " pvp.id as id, pvp.active as active, pvp.description as description, pvp." + sqlGenerator.escape("key") + " as " + + sqlGenerator.escape("key") + "" + " from m_password_validation_policy pvp"; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PermissionReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PermissionReadPlatformServiceImpl.java index fdb03ab82..3e8d96e9a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PermissionReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/PermissionReadPlatformServiceImpl.java @@ -22,6 +22,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.useradministration.data.PermissionData; import org.slf4j.Logger; @@ -37,12 +38,15 @@ public class PermissionReadPlatformServiceImpl implements PermissionReadPlatform private static final Logger LOG = LoggerFactory.getLogger(PermissionReadPlatformServiceImpl.class); private final JdbcTemplate jdbcTemplate; + private final DatabaseSpecificSQLGenerator sqlGenerator; private final PlatformSecurityContext context; @Autowired - public PermissionReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource) { + public PermissionReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, + DatabaseSpecificSQLGenerator sqlGenerator) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.sqlGenerator = sqlGenerator; } @Override @@ -50,7 +54,7 @@ public class PermissionReadPlatformServiceImpl implements PermissionReadPlatform this.context.authenticatedUser(); - final PermissionUsageDataMapper mapper = new PermissionUsageDataMapper(); + final PermissionUsageDataMapper mapper = new PermissionUsageDataMapper(sqlGenerator); final String sql = mapper.permissionSchema(); LOG.info("retrieveAllPermissions: {}", sql); return this.jdbcTemplate.query(sql, mapper, new Object[] {}); @@ -61,7 +65,7 @@ public class PermissionReadPlatformServiceImpl implements PermissionReadPlatform this.context.authenticatedUser(); - final PermissionUsageDataMapper mapper = new PermissionUsageDataMapper(); + final PermissionUsageDataMapper mapper = new PermissionUsageDataMapper(sqlGenerator); final String sql = mapper.makerCheckerablePermissionSchema(); LOG.info("retrieveAllMakerCheckerablePermissions: {}", sql); @@ -71,7 +75,7 @@ public class PermissionReadPlatformServiceImpl implements PermissionReadPlatform @Override public Collection retrieveAllRolePermissions(final Long roleId) { - final PermissionUsageDataMapper mapper = new PermissionUsageDataMapper(); + final PermissionUsageDataMapper mapper = new PermissionUsageDataMapper(sqlGenerator); final String sql = mapper.rolePermissionSchema(); LOG.info("retrieveAllRolePermissions: {}", sql); @@ -80,6 +84,12 @@ public class PermissionReadPlatformServiceImpl implements PermissionReadPlatform private static final class PermissionUsageDataMapper implements RowMapper { + private final DatabaseSpecificSQLGenerator sqlGenerator; + + PermissionUsageDataMapper(DatabaseSpecificSQLGenerator sqlGenerator) { + this.sqlGenerator = sqlGenerator; + } + @Override public PermissionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { @@ -106,7 +116,8 @@ public class PermissionReadPlatformServiceImpl implements PermissionReadPlatform */ return "select p.grouping, p.code, p.entity_name as entityName, p.action_name as actionName, p.can_maker_checker as selected" - + " from m_permission p " + " where `grouping` != 'special' and code not like 'READ_%' and code not like '%\\_CHECKER'" + + " from m_permission p " + " where " + sqlGenerator.escape("grouping") + + " != 'special' and code not like 'READ_%' and code not like '%\\_CHECKER'" + " order by p.grouping, coalesce(entity_name, ''), p.code"; } diff --git a/fineract-provider/src/main/resources/db/changelog/db.changelog-master.xml b/fineract-provider/src/main/resources/db/changelog/db.changelog-master.xml index 7e5cd0f97..5bc9e3cd3 100644 --- a/fineract-provider/src/main/resources/db/changelog/db.changelog-master.xml +++ b/fineract-provider/src/main/resources/db/changelog/db.changelog-master.xml @@ -23,6 +23,7 @@ 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.1.xsd"> + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml b/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml index 05449fa33..ae142fd40 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant-store/changelog-tenant-store.xml @@ -22,6 +22,5 @@ - - + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant-store/parts/0003_reset_postgresql_sequences.xml b/fineract-provider/src/main/resources/db/changelog/tenant-store/parts/0003_reset_postgresql_sequences.xml new file mode 100644 index 000000000..b2ea722e9 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant-store/parts/0003_reset_postgresql_sequences.xml @@ -0,0 +1,32 @@ + + + + + + SELECT SETVAL('tenant_server_connections_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM tenant_server_connections; + SELECT SETVAL('tenants_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM tenants; + SELECT SETVAL('timezones_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM timezones; + + + 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 05449fa33..0248acf8d 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 @@ -22,6 +22,6 @@ - - + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0003_postgresql_specific_initial_data.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0003_postgresql_specific_initial_data.xml new file mode 100644 index 000000000..96e19ebe8 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0003_postgresql_specific_initial_data.xml @@ -0,0 +1,1972 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT SETVAL('c_cache_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM c_cache; + SELECT SETVAL('c_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM c_configuration; + SELECT SETVAL('c_external_service_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM c_external_service; + SELECT SETVAL('job_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM job; + SELECT SETVAL('job_parameters_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM job_parameters; + SELECT SETVAL('m_appuser_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_appuser; + SELECT SETVAL('m_code_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_code; + SELECT SETVAL('m_code_value_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_code_value; + SELECT SETVAL('m_creditbureau_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_creditbureau; + SELECT SETVAL('m_creditbureau_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_creditbureau_configuration; + SELECT SETVAL('m_currency_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_currency; + SELECT SETVAL('m_entity_relation_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_entity_relation; + SELECT SETVAL('m_field_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_field_configuration; + SELECT SETVAL('m_group_level_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_group_level; + SELECT SETVAL('m_hook_schema_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_hook_schema; + SELECT SETVAL('m_hook_templates_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_hook_templates; + SELECT SETVAL('m_office_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_office; + SELECT SETVAL('m_organisation_currency_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_organisation_currency; + SELECT SETVAL('m_password_validation_policy_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_password_validation_policy; + SELECT SETVAL('m_payment_type_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_payment_type; + SELECT SETVAL('m_permission_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_permission; + SELECT SETVAL('m_provision_category_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_provision_category; + SELECT SETVAL('m_report_mailing_job_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_report_mailing_job_configuration; + SELECT SETVAL('m_role_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_role; + SELECT SETVAL('m_working_days_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM m_working_days; + SELECT SETVAL('mix_taxonomy_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM mix_taxonomy; + SELECT SETVAL('mix_taxonomy_mapping_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM mix_taxonomy_mapping; + SELECT SETVAL('mix_xbrl_namespace_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM mix_xbrl_namespace; + SELECT SETVAL('ppi_scores_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM ppi_scores; + SELECT SETVAL('ref_loan_transaction_processing_strategy_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM ref_loan_transaction_processing_strategy; + SELECT SETVAL('scheduled_email_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM scheduled_email_configuration; + SELECT SETVAL('scheduler_detail_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM scheduler_detail; + SELECT SETVAL('topic_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM topic; + SELECT SETVAL('topic_subscriber_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM topic_subscriber; + SELECT SETVAL('stretchy_parameter_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM stretchy_parameter; + SELECT SETVAL('stretchy_report_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM stretchy_report; + SELECT SETVAL('stretchy_report_parameter_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM stretchy_report_parameter; + SELECT SETVAL('twofactor_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM twofactor_configuration; + + + + + + + + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0004_camelcase_column_renaming.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0004_camelcase_column_renaming.xml new file mode 100644 index 000000000..f67cfa077 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0004_camelcase_column_renaming.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/configuration/spring/TestsWithoutDatabaseAndNoJobsConfiguration.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/configuration/spring/TestsWithoutDatabaseAndNoJobsConfiguration.java index de35c63d2..d1c7ae8d4 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/configuration/spring/TestsWithoutDatabaseAndNoJobsConfiguration.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/configuration/spring/TestsWithoutDatabaseAndNoJobsConfiguration.java @@ -19,7 +19,9 @@ package org.apache.fineract.infrastructure.configuration.spring; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import com.zaxxer.hikari.HikariConfig; import javax.sql.DataSource; import org.apache.fineract.infrastructure.core.boot.AbstractApplicationConfiguration; import org.apache.fineract.infrastructure.core.config.FineractProperties; @@ -42,6 +44,13 @@ import org.springframework.security.crypto.password.PasswordEncoder; @EnableConfigurationProperties({ FineractProperties.class }) public class TestsWithoutDatabaseAndNoJobsConfiguration extends AbstractApplicationConfiguration { + @Bean + public HikariConfig hikariConfig() { + HikariConfig mock = mock(HikariConfig.class); + when(mock.getDriverClassName()).thenReturn("org.mariadb.jdbc.Driver"); + return mock; + } + @Bean public TenantDataSourceFactory tenantDataSourceFactory() { return new TenantDataSourceFactory(null) {