You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2023/06/06 09:45:31 UTC

[fineract] 14/15: FINERACT-1724: Backward compatibility fix for MariaDB on sequence vs table incrementers + SQL grammar fix for Loan COB catchup

This is an automated email from the ASF dual-hosted git repository.

arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git

commit c437c5a48b802ca8be956a42c304f633f461ce9d
Author: Arnold Galovics <ga...@gmail.com>
AuthorDate: Mon Jun 5 15:35:16 2023 +0200

    FINERACT-1724: Backward compatibility fix for MariaDB on sequence vs table incrementers + SQL grammar fix for Loan COB catchup
---
 .../database/DatabaseSpecificSQLGenerator.java     |  6 +-
 .../jobs/ScheduledJobRunnerConfig.java             | 10 +++
 .../jobs/config/FineractBatchConfiguration.java    | 32 ---------
 ...ineractDataFieldMaxValueIncrementerFactory.java | 83 ++++++++++++++++++++++
 .../jobs/domain/JobExecutionRepository.java        |  2 +-
 5 files changed, 97 insertions(+), 36 deletions(-)

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
index 7e64b4794..fc9591992 100644
--- 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
@@ -160,11 +160,11 @@ public class DatabaseSpecificSQLGenerator {
         }
     }
 
-    public String castBigInt(String sql) {
+    public String castInteger(String sql) {
         if (databaseTypeResolver.isMySQL()) {
-            return format("CAST(%s AS BIGINT)", sql);
+            return format("CAST(%s AS SIGNED INTEGER)", sql);
         } else if (databaseTypeResolver.isPostgreSQL()) {
-            return format("%s::BIGINT", sql);
+            return format("%s::INTEGER", sql);
         } else {
             throw new IllegalStateException("Database type is not supported for casting to bigint " + databaseTypeResolver.databaseType());
         }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.java
index a9b69684d..ca7a0b552 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.java
@@ -22,6 +22,7 @@ import java.util.List;
 import org.apache.fineract.infrastructure.core.persistence.ExtendedJpaTransactionManager;
 import org.apache.fineract.infrastructure.core.persistence.TransactionLifecycleCallback;
 import org.apache.fineract.infrastructure.core.service.database.RoutingDataSource;
+import org.apache.fineract.infrastructure.jobs.config.FineractDataFieldMaxValueIncrementerFactory;
 import org.springframework.batch.core.configuration.JobRegistry;
 import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
 import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
@@ -31,6 +32,7 @@ import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher;
 import org.springframework.batch.core.repository.JobRepository;
 import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
 import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
+import org.springframework.batch.item.database.support.DataFieldMaxValueIncrementerFactory;
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
 import org.springframework.context.annotation.Bean;
@@ -56,6 +58,13 @@ public class ScheduledJobRunnerConfig {
         return new Jackson2ExecutionContextStringSerializer();
     }
 
+    @Bean
+    public DataFieldMaxValueIncrementerFactory incrementerFactory(RoutingDataSource routingDataSource) {
+        // The DefaultDataFieldMaxValueIncrementerFactory has to be overridden because Spring 6 introduced
+        // a new MariaDB incrementer that's incompatible with Spring Batch 4.x
+        return new FineractDataFieldMaxValueIncrementerFactory(routingDataSource);
+    }
+
     @Bean
     public JobRepository jobRepository(RoutingDataSource routingDataSource, PlatformTransactionManager transactionManager)
             throws Exception {
@@ -64,6 +73,7 @@ public class ScheduledJobRunnerConfig {
         factory.setTransactionManager(transactionManager);
         factory.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
         factory.setSerializer(executionContextSerializer());
+        factory.setIncrementerFactory(incrementerFactory(routingDataSource));
         factory.afterPropertiesSet();
         return factory.getObject();
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/config/FineractBatchConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/config/FineractBatchConfiguration.java
deleted file mode 100644
index ed8ca3ae3..000000000
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/config/FineractBatchConfiguration.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * 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.jobs.config;
-
-import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
-import org.springframework.batch.core.repository.ExecutionContextSerializer;
-import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
-
-//@Configuration(proxyBeanMethods = false)
-public class FineractBatchConfiguration extends DefaultBatchConfiguration {
-
-    @Override
-    protected ExecutionContextSerializer getExecutionContextSerializer() {
-        return new Jackson2ExecutionContextStringSerializer();
-    }
-}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/config/FineractDataFieldMaxValueIncrementerFactory.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/config/FineractDataFieldMaxValueIncrementerFactory.java
new file mode 100644
index 000000000..8efe5476a
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/config/FineractDataFieldMaxValueIncrementerFactory.java
@@ -0,0 +1,83 @@
+/**
+ * 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.jobs.config;
+
+import static org.springframework.batch.support.DatabaseType.MARIADB;
+import static org.springframework.batch.support.DatabaseType.MYSQL;
+import static org.springframework.batch.support.DatabaseType.POSTGRES;
+
+import java.util.List;
+import javax.sql.DataSource;
+import lombok.RequiredArgsConstructor;
+import org.springframework.batch.item.database.support.DataFieldMaxValueIncrementerFactory;
+import org.springframework.batch.support.DatabaseType;
+import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
+import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
+import org.springframework.jdbc.support.incrementer.PostgresSequenceMaxValueIncrementer;
+
+@RequiredArgsConstructor
+public class FineractDataFieldMaxValueIncrementerFactory implements DataFieldMaxValueIncrementerFactory {
+
+    private static final List<DatabaseType> SUPPORTED_DATABASE_TYPES = List.of(MARIADB, MYSQL, POSTGRES);
+
+    private final DataSource dataSource;
+
+    private String incrementerColumnName = "ID";
+
+    /**
+     * Public setter for the column name (defaults to "ID") in the incrementer. Only used by some platforms (Derby,
+     * HSQL, MySQL, SQL Server and Sybase), and should be fine for use with Spring Batch meta data as long as the
+     * default batch schema hasn't been changed.
+     *
+     * @param incrementerColumnName
+     *            the primary key column name to set
+     */
+    public void setIncrementerColumnName(String incrementerColumnName) {
+        this.incrementerColumnName = incrementerColumnName;
+    }
+
+    @Override
+    public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) {
+        DatabaseType databaseType = getDatabaseType(incrementerType);
+        if (databaseType == MYSQL || databaseType == MARIADB) {
+            MySQLMaxValueIncrementer mySQLMaxValueIncrementer = new MySQLMaxValueIncrementer(dataSource, incrementerName,
+                    incrementerColumnName);
+            mySQLMaxValueIncrementer.setUseNewConnection(true);
+            return mySQLMaxValueIncrementer;
+        } else if (databaseType == POSTGRES) {
+            return new PostgresSequenceMaxValueIncrementer(dataSource, incrementerName);
+        }
+        throw new IllegalArgumentException("databaseType argument was not on the approved list");
+    }
+
+    @Override
+    public boolean isSupportedIncrementerType(String incrementerType) {
+        DatabaseType databaseType = getDatabaseType(incrementerType);
+        return SUPPORTED_DATABASE_TYPES.contains(databaseType);
+    }
+
+    @Override
+    public String[] getSupportedIncrementerTypes() {
+        return SUPPORTED_DATABASE_TYPES.stream().map(DatabaseType::name).toArray(String[]::new);
+    }
+
+    private DatabaseType getDatabaseType(String incrementerType) {
+        return DatabaseType.valueOf(incrementerType.toUpperCase());
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/JobExecutionRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/JobExecutionRepository.java
index 3b2ee3dd4..0db04e4ac 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/JobExecutionRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/JobExecutionRepository.java
@@ -172,7 +172,7 @@ public class JobExecutionRepository implements InitializingBean {
         sqlStatementBuilder.append(
                 "SELECT bje.JOB_EXECUTION_ID FROM BATCH_JOB_INSTANCE bji INNER JOIN BATCH_JOB_EXECUTION bje ON bji.JOB_INSTANCE_ID = bje.JOB_INSTANCE_ID INNER JOIN BATCH_JOB_EXECUTION_PARAMS bjep ON bje.JOB_EXECUTION_ID = bjep.JOB_EXECUTION_ID"
                         + " WHERE bje.STATUS IN (:statuses) AND bji.JOB_NAME = :jobName AND bjep.PARAMETER_NAME = :jobCustomParamKeyName AND "
-                        + sqlGenerator.castBigInt("bjep.PARAMETER_VALUE") + " IN (" + getSubQueryForCustomJobParameters()
+                        + sqlGenerator.castInteger("bjep.PARAMETER_VALUE") + " IN (" + getSubQueryForCustomJobParameters()
                         + ") AND bje.JOB_INSTANCE_ID NOT IN (SELECT bje.JOB_INSTANCE_ID FROM BATCH_JOB_INSTANCE bji INNER JOIN BATCH_JOB_EXECUTION bje ON bji.JOB_INSTANCE_ID = bje.JOB_INSTANCE_ID"
                         + " WHERE bje.STATUS = :completedStatus AND bji.JOB_NAME = :jobName)");
         return namedParameterJdbcTemplate.queryForList(