Skip to content

Commit

Permalink
BATCH-2515: Integrate SAP Hana as supported Spring Batch database
Browse files Browse the repository at this point in the history
This commit adds SAP HANA as a supported Spring Batch database, enabling
developers to seamlessly move their existing Spring Batch projects to SAP HANA
or easily starting new Spring Batch projects on SAP HANA.

This commit contains the following changes:

- Add SAP HANA to the DatabaseType enum
- Add HanaPagingQueryProvider and tests
- Add properties files for SAP HANA
  • Loading branch information
breglerj committed Sep 18, 2020
1 parent 743da96 commit 508fff4
Show file tree
Hide file tree
Showing 20 changed files with 547 additions and 4 deletions.
17 changes: 17 additions & 0 deletions spring-batch-core/src/main/resources/batch-hana.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Placeholders batch.*
# for SAP HANA:
batch.jdbc.driver=com.sap.db.jdbc.Driver
batch.jdbc.url=jdbc:sap://localhost:39015/
batch.jdbc.user=SPRING_TEST
batch.jdbc.password=Spr1ng_test
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer
batch.schema.script=classpath:/org/springframework/batch/core/schema-hana.sql
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-hana.sql
batch.jdbc.testWhileIdle=true
batch.jdbc.validationQuery=


# Non-platform dependent settings that you might like to change
batch.data.source.init=true
batch.table.prefix=BATCH_

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

-- create the requisite table

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
JOB_EXECUTION_ID BIGINT NOT NULL ,
TYPE_CD VARCHAR(6) NOT NULL ,
KEY_NAME VARCHAR(100) NOT NULL ,
STRING_VAL VARCHAR(250) ,
DATE_VAL TIMESTAMP DEFAULT NULL ,
LONG_VAL BIGINT ,
DOUBLE_VAL DOUBLE ,
IDENTIFYING VARCHAR(1) NOT NULL ,
constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
);

-- insert script that 'copies' existing batch_job_params to batch_job_execution_params
-- sets new params to identifying ones
-- verified on h2,

INSERT INTO BATCH_JOB_EXECUTION_PARAMS
( JOB_EXECUTION_ID , TYPE_CD, KEY_NAME, STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL, IDENTIFYING )
SELECT
JE.JOB_EXECUTION_ID , JP.TYPE_CD , JP.KEY_NAME , JP.STRING_VAL , JP.DATE_VAL , JP.LONG_VAL , JP.DOUBLE_VAL , 'Y'
FROM
BATCH_JOB_PARAMS JP,BATCH_JOB_EXECUTION JE
WHERE
JP.JOB_INSTANCE_ID = JE.JOB_INSTANCE_ID;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Autogenerated: do not edit this file
DROP TABLE BATCH_STEP_EXECUTION_CONTEXT ;
DROP TABLE BATCH_JOB_EXECUTION_CONTEXT ;
DROP TABLE BATCH_JOB_EXECUTION_PARAMS ;
DROP TABLE BATCH_STEP_EXECUTION ;
DROP TABLE BATCH_JOB_EXECUTION ;
DROP TABLE BATCH_JOB_INSTANCE ;

DROP SEQUENCE BATCH_STEP_EXECUTION_SEQ ;
DROP SEQUENCE BATCH_JOB_EXECUTION_SEQ ;
DROP SEQUENCE BATCH_JOB_SEQ ;
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
-- Autogenerated: do not edit this file

CREATE TABLE BATCH_JOB_INSTANCE (
JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
VERSION BIGINT ,
JOB_NAME VARCHAR(100) NOT NULL,
JOB_KEY VARCHAR(32) NOT NULL,
constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ;

CREATE TABLE BATCH_JOB_EXECUTION (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
VERSION BIGINT ,
JOB_INSTANCE_ID BIGINT NOT NULL,
CREATE_TIME TIMESTAMP NOT NULL,
START_TIME TIMESTAMP DEFAULT NULL ,
END_TIME TIMESTAMP DEFAULT NULL ,
STATUS VARCHAR(10) ,
EXIT_CODE VARCHAR(2500) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED TIMESTAMP,
JOB_CONFIGURATION_LOCATION VARCHAR(2500) ,
constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ;

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
JOB_EXECUTION_ID BIGINT NOT NULL ,
TYPE_CD VARCHAR(6) NOT NULL ,
KEY_NAME VARCHAR(100) NOT NULL ,
STRING_VAL VARCHAR(250) ,
DATE_VAL TIMESTAMP DEFAULT NULL ,
LONG_VAL BIGINT ,
DOUBLE_VAL DOUBLE ,
IDENTIFYING VARCHAR(1) NOT NULL ,
constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

CREATE TABLE BATCH_STEP_EXECUTION (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
VERSION BIGINT NOT NULL,
STEP_NAME VARCHAR(100) NOT NULL,
JOB_EXECUTION_ID BIGINT NOT NULL,
START_TIME TIMESTAMP NOT NULL ,
END_TIME TIMESTAMP DEFAULT NULL ,
STATUS VARCHAR(10) ,
COMMIT_COUNT BIGINT ,
READ_COUNT BIGINT ,
FILTER_COUNT BIGINT ,
WRITE_COUNT BIGINT ,
READ_SKIP_COUNT BIGINT ,
WRITE_SKIP_COUNT BIGINT ,
PROCESS_SKIP_COUNT BIGINT ,
ROLLBACK_COUNT BIGINT ,
EXIT_CODE VARCHAR(2500) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED TIMESTAMP,
constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT CLOB ,
constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ;

CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT CLOB ,
constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 0 MINVALUE 0 NO CYCLE;
CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 0 MINVALUE 0 NO CYCLE;
CREATE SEQUENCE BATCH_JOB_SEQ START WITH 0 MINVALUE 0 NO CYCLE;
14 changes: 14 additions & 0 deletions spring-batch-core/src/main/sql/hana.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
platform=hana
# SQL language oddities
BIGINT = BIGINT
IDENTITY =
GENERATED = GENERATED BY DEFAULT AS IDENTITY
IFEXISTSBEFORE =
DOUBLE = DOUBLE
BLOB = BLOB
CLOB = CLOB
TIMESTAMP = TIMESTAMP
VARCHAR = VARCHAR
CHAR = VARCHAR
# for generating drop statements...
SEQUENCE = SEQUENCE
3 changes: 3 additions & 0 deletions spring-batch-core/src/main/sql/hana.vpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#macro (sequence $name $value)CREATE SEQUENCE ${name} START WITH ${value} MINVALUE 0 NO CYCLE;
#end
#macro (notnull $name $type)ALTER (${name} ${type} NOT NULL)#end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Placeholders batch.*
# for SAP HANA:
batch.jdbc.driver=com.sap.db.jdbc.Driver
batch.jdbc.url=jdbc:sap://localhost:39015/
batch.jdbc.user=SPRING_TEST
batch.jdbc.password=Spr1ng_test
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.schema.script=classpath:org/springframework/batch/item/database/init-foo-schema-hana.sql
batch.business.schema.script=classpath:/org/springframework/batch/jms/init.sql
batch.data.source.init=true
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer
batch.database.incrementer.parent=sequenceIncrementerParent
batch.verify.cursor.position=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
DROP TABLE T_FOOS;
DROP TABLE T_WRITE_FOOS;

CREATE TABLE T_FOOS (
ID BIGINT NOT NULL,
NAME VARCHAR(45),
CODE VARCHAR(10),
VALUE BIGINT
);

ALTER TABLE T_FOOS ADD PRIMARY KEY (ID);

INSERT INTO t_foos (id, name, value) VALUES (1, 'bar2', 2);
INSERT INTO t_foos (id, name, value) VALUES (2, 'bar4', 4);
INSERT INTO t_foos (id, name, value) VALUES (3, 'bar1', 1);
INSERT INTO t_foos (id, name, value) VALUES (4, 'bar5', 5);
INSERT INTO t_foos (id, name, value) VALUES (5, 'bar3', 3);

CREATE TABLE T_WRITE_FOOS (
ID BIGINT NOT NULL,
NAME VARCHAR(45),
VALUE BIGINT
);

ALTER TABLE T_WRITE_FOOS ADD PRIMARY KEY (ID);
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.batch.item.database.support.Db2PagingQueryProvider;
import org.springframework.batch.item.database.support.DerbyPagingQueryProvider;
import org.springframework.batch.item.database.support.H2PagingQueryProvider;
import org.springframework.batch.item.database.support.HanaPagingQueryProvider;
import org.springframework.batch.item.database.support.HsqlPagingQueryProvider;
import org.springframework.batch.item.database.support.MySqlPagingQueryProvider;
import org.springframework.batch.item.database.support.OraclePagingQueryProvider;
Expand Down Expand Up @@ -359,6 +360,7 @@ private PagingQueryProvider determineQueryProvider(DataSource dataSource) {
case DB2ZOS:
case DB2AS400: provider = new Db2PagingQueryProvider(); break;
case H2: provider = new H2PagingQueryProvider(); break;
case HANA: provider = new HanaPagingQueryProvider(); break;
case HSQL: provider = new HsqlPagingQueryProvider(); break;
case SQLSERVER: provider = new SqlServerPagingQueryProvider(); break;
case MYSQL: provider = new MySqlPagingQueryProvider(); break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.H2SequenceMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer;
Expand All @@ -37,6 +38,7 @@
import static org.springframework.batch.support.DatabaseType.DB2ZOS;
import static org.springframework.batch.support.DatabaseType.DERBY;
import static org.springframework.batch.support.DatabaseType.H2;
import static org.springframework.batch.support.DatabaseType.HANA;
import static org.springframework.batch.support.DatabaseType.HSQL;
import static org.springframework.batch.support.DatabaseType.MYSQL;
import static org.springframework.batch.support.DatabaseType.ORACLE;
Expand Down Expand Up @@ -98,6 +100,9 @@ else if (databaseType == HSQL) {
else if (databaseType == H2) {
return new H2SequenceMaxValueIncrementer(dataSource, incrementerName);
}
else if (databaseType == HANA) {
return new HanaSequenceMaxValueIncrementer(dataSource, incrementerName);
}
else if (databaseType == MYSQL) {
MySQLMaxValueIncrementer mySQLMaxValueIncrementer = new MySQLMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
mySQLMaxValueIncrementer.setUseNewConnection(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2006-2018 the original author or authors.
*
* Licensed 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.springframework.batch.item.database.support;

import org.springframework.batch.item.database.PagingQueryProvider;
import org.springframework.util.StringUtils;

/**
* SAP HANA implementation of a {@link PagingQueryProvider} using database specific features.
*
* @author Jonathan Bregler
* @since 4.0
*/
public class HanaPagingQueryProvider extends AbstractSqlPagingQueryProvider {

@Override
public String generateFirstPageQuery(int pageSize) {
return SqlPagingQueryUtils.generateLimitSqlQuery(this, false, buildLimitClause(pageSize));
}

@Override
public String generateRemainingPagesQuery(int pageSize) {
if(StringUtils.hasText(getGroupClause())) {
return SqlPagingQueryUtils.generateLimitGroupedSqlQuery(this, true, buildLimitClause(pageSize));
}
else {
return SqlPagingQueryUtils.generateLimitSqlQuery(this, true, buildLimitClause(pageSize));
}
}

private String buildLimitClause(int pageSize) {
return new StringBuilder().append("LIMIT ").append(pageSize).toString();
}

@Override
public String generateJumpToItemQuery(int itemIndex, int pageSize) {
int page = itemIndex / pageSize;
int offset = (page * pageSize) - 1;
offset = offset<0 ? 0 : offset;
String limitClause = new StringBuilder().append("LIMIT 1 OFFSET ").append(offset).toString();
return SqlPagingQueryUtils.generateLimitJumpToQuery(this, limitClause);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2012 the original author or authors.
* Copyright 2006-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,7 @@
import static org.springframework.batch.support.DatabaseType.DB2AS400;
import static org.springframework.batch.support.DatabaseType.DERBY;
import static org.springframework.batch.support.DatabaseType.H2;
import static org.springframework.batch.support.DatabaseType.HANA;
import static org.springframework.batch.support.DatabaseType.HSQL;
import static org.springframework.batch.support.DatabaseType.MYSQL;
import static org.springframework.batch.support.DatabaseType.ORACLE;
Expand Down Expand Up @@ -78,6 +79,7 @@ public class SqlPagingQueryProviderFactoryBean implements FactoryBean<PagingQuer
providers.put(DERBY,new DerbyPagingQueryProvider());
providers.put(HSQL,new HsqlPagingQueryProvider());
providers.put(H2,new H2PagingQueryProvider());
providers.put(HANA,new HanaPagingQueryProvider());
providers.put(MYSQL,new MySqlPagingQueryProvider());
providers.put(ORACLE,new OraclePagingQueryProvider());
providers.put(POSTGRES,new PostgresPagingQueryProvider());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007 the original author or authors.
* Copyright 2006-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -47,7 +47,8 @@ public enum DatabaseType {
POSTGRES("PostgreSQL"),
SYBASE("Sybase"),
H2("H2"),
SQLITE("SQLite");
SQLITE("SQLite"),
HANA("HDB");

private static final Map<String, DatabaseType> nameMap;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.springframework.jdbc.support.incrementer.Db2LuwMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.Db2MainframeMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer;
Expand Down Expand Up @@ -62,6 +63,7 @@ public void testSupportedDatabaseType(){
assertTrue(factory.isSupportedIncrementerType("sqlserver"));
assertTrue(factory.isSupportedIncrementerType("sybase"));
assertTrue(factory.isSupportedIncrementerType("sqlite"));
assertTrue(factory.isSupportedIncrementerType("hana"));
}

public void testUnsupportedDatabaseType(){
Expand Down Expand Up @@ -128,5 +130,9 @@ public void testSybase(){
public void testSqlite(){
assertTrue(factory.getIncrementer("sqlite", "NAME") instanceof SqliteMaxValueIncrementer);
}

public void testHana(){
assertTrue(factory.getIncrementer("hana", "NAME") instanceof HanaSequenceMaxValueIncrementer);
}

}

0 comments on commit 508fff4

Please sign in to comment.