Skip to content

Commit c427833

Browse files
committed
BATCH-2515: Integrate SAP Hana as supported Spring Batch database
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 - Increase Spring version to 5.0.5 which is the first version to include HanaSequenceMaxValueIncrementer (see https://jira.spring.io/browse/SPR-16558)
1 parent 6c54767 commit c427833

File tree

21 files changed

+549
-6
lines changed

21 files changed

+549
-6
lines changed

spring-batch-core/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ task generateSql {
2222
classpath: configurations.vpp.asPath)
2323

2424
doLast {
25-
['db2', 'derby', 'h2', 'hsqldb', 'mysql',
25+
['db2', 'derby', 'h2', 'hana', 'hsqldb', 'mysql',
2626
'oracle10g', 'postgresql', 'sqlf', 'sqlserver', 'sybase'].each { dbType ->
2727
ant.vppcopy(todir: generatedResourcesDir, overwrite: 'true') {
2828
config {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Placeholders batch.*
2+
# for SAP HANA:
3+
batch.jdbc.driver=com.sap.db.jdbc.Driver
4+
batch.jdbc.url=jdbc:sap://localhost:39015/
5+
batch.jdbc.user=SPRING_TEST
6+
batch.jdbc.password=Spr1ng_test
7+
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer
8+
batch.schema.script=classpath:/org/springframework/batch/core/schema-hana.sql
9+
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-hana.sql
10+
batch.jdbc.testWhileIdle=true
11+
batch.jdbc.validationQuery=
12+
13+
14+
# Non-platform dependent settings that you might like to change
15+
batch.data.source.init=true
16+
batch.table.prefix=BATCH_
17+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
-- create the requisite table
3+
4+
CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
5+
JOB_EXECUTION_ID BIGINT NOT NULL ,
6+
TYPE_CD VARCHAR(6) NOT NULL ,
7+
KEY_NAME VARCHAR(100) NOT NULL ,
8+
STRING_VAL VARCHAR(250) ,
9+
DATE_VAL TIMESTAMP DEFAULT NULL ,
10+
LONG_VAL BIGINT ,
11+
DOUBLE_VAL DOUBLE ,
12+
IDENTIFYING VARCHAR(1) NOT NULL ,
13+
constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
14+
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
15+
);
16+
17+
-- insert script that 'copies' existing batch_job_params to batch_job_execution_params
18+
-- sets new params to identifying ones
19+
-- verified on h2,
20+
21+
INSERT INTO BATCH_JOB_EXECUTION_PARAMS
22+
( JOB_EXECUTION_ID , TYPE_CD, KEY_NAME, STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL, IDENTIFYING )
23+
SELECT
24+
JE.JOB_EXECUTION_ID , JP.TYPE_CD , JP.KEY_NAME , JP.STRING_VAL , JP.DATE_VAL , JP.LONG_VAL , JP.DOUBLE_VAL , 'Y'
25+
FROM
26+
BATCH_JOB_PARAMS JP,BATCH_JOB_EXECUTION JE
27+
WHERE
28+
JP.JOB_INSTANCE_ID = JE.JOB_INSTANCE_ID;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Autogenerated: do not edit this file
2+
DROP TABLE BATCH_STEP_EXECUTION_CONTEXT ;
3+
DROP TABLE BATCH_JOB_EXECUTION_CONTEXT ;
4+
DROP TABLE BATCH_JOB_EXECUTION_PARAMS ;
5+
DROP TABLE BATCH_STEP_EXECUTION ;
6+
DROP TABLE BATCH_JOB_EXECUTION ;
7+
DROP TABLE BATCH_JOB_INSTANCE ;
8+
9+
DROP SEQUENCE BATCH_STEP_EXECUTION_SEQ ;
10+
DROP SEQUENCE BATCH_JOB_EXECUTION_SEQ ;
11+
DROP SEQUENCE BATCH_JOB_SEQ ;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
-- Autogenerated: do not edit this file
2+
3+
CREATE TABLE BATCH_JOB_INSTANCE (
4+
JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
5+
VERSION BIGINT ,
6+
JOB_NAME VARCHAR(100) NOT NULL,
7+
JOB_KEY VARCHAR(32) NOT NULL,
8+
constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
9+
) ;
10+
11+
CREATE TABLE BATCH_JOB_EXECUTION (
12+
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
13+
VERSION BIGINT ,
14+
JOB_INSTANCE_ID BIGINT NOT NULL,
15+
CREATE_TIME TIMESTAMP NOT NULL,
16+
START_TIME TIMESTAMP DEFAULT NULL ,
17+
END_TIME TIMESTAMP DEFAULT NULL ,
18+
STATUS VARCHAR(10) ,
19+
EXIT_CODE VARCHAR(2500) ,
20+
EXIT_MESSAGE VARCHAR(2500) ,
21+
LAST_UPDATED TIMESTAMP,
22+
JOB_CONFIGURATION_LOCATION VARCHAR(2500) ,
23+
constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
24+
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
25+
) ;
26+
27+
CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
28+
JOB_EXECUTION_ID BIGINT NOT NULL ,
29+
TYPE_CD VARCHAR(6) NOT NULL ,
30+
KEY_NAME VARCHAR(100) NOT NULL ,
31+
STRING_VAL VARCHAR(250) ,
32+
DATE_VAL TIMESTAMP DEFAULT NULL ,
33+
LONG_VAL BIGINT ,
34+
DOUBLE_VAL DOUBLE ,
35+
IDENTIFYING VARCHAR(1) NOT NULL ,
36+
constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
37+
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
38+
) ;
39+
40+
CREATE TABLE BATCH_STEP_EXECUTION (
41+
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
42+
VERSION BIGINT NOT NULL,
43+
STEP_NAME VARCHAR(100) NOT NULL,
44+
JOB_EXECUTION_ID BIGINT NOT NULL,
45+
START_TIME TIMESTAMP NOT NULL ,
46+
END_TIME TIMESTAMP DEFAULT NULL ,
47+
STATUS VARCHAR(10) ,
48+
COMMIT_COUNT BIGINT ,
49+
READ_COUNT BIGINT ,
50+
FILTER_COUNT BIGINT ,
51+
WRITE_COUNT BIGINT ,
52+
READ_SKIP_COUNT BIGINT ,
53+
WRITE_SKIP_COUNT BIGINT ,
54+
PROCESS_SKIP_COUNT BIGINT ,
55+
ROLLBACK_COUNT BIGINT ,
56+
EXIT_CODE VARCHAR(2500) ,
57+
EXIT_MESSAGE VARCHAR(2500) ,
58+
LAST_UPDATED TIMESTAMP,
59+
constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
60+
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
61+
) ;
62+
63+
CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
64+
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
65+
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
66+
SERIALIZED_CONTEXT CLOB ,
67+
constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
68+
references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
69+
) ;
70+
71+
CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
72+
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
73+
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
74+
SERIALIZED_CONTEXT CLOB ,
75+
constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
76+
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
77+
) ;
78+
79+
CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 0 MINVALUE 0 NO CYCLE;
80+
CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 0 MINVALUE 0 NO CYCLE;
81+
CREATE SEQUENCE BATCH_JOB_SEQ START WITH 0 MINVALUE 0 NO CYCLE;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
platform=hana
2+
# SQL language oddities
3+
BIGINT = BIGINT
4+
IDENTITY =
5+
GENERATED = GENERATED BY DEFAULT AS IDENTITY
6+
IFEXISTSBEFORE =
7+
DOUBLE = DOUBLE
8+
BLOB = BLOB
9+
CLOB = CLOB
10+
TIMESTAMP = TIMESTAMP
11+
VARCHAR = VARCHAR
12+
CHAR = VARCHAR
13+
# for generating drop statements...
14+
SEQUENCE = SEQUENCE
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#macro (sequence $name $value)CREATE SEQUENCE ${name} START WITH ${value} MINVALUE 0 NO CYCLE;
2+
#end
3+
#macro (notnull $name $type)ALTER (${name} ${type} NOT NULL)#end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Placeholders batch.*
2+
# for SAP HANA:
3+
batch.jdbc.driver=com.sap.db.jdbc.Driver
4+
batch.jdbc.url=jdbc:sap://localhost:39015/
5+
batch.jdbc.user=SPRING_TEST
6+
batch.jdbc.password=Spr1ng_test
7+
batch.jdbc.testWhileIdle=false
8+
batch.jdbc.validationQuery=
9+
batch.schema.script=classpath:org/springframework/batch/item/database/init-foo-schema-hana.sql
10+
batch.business.schema.script=classpath:/org/springframework/batch/jms/init.sql
11+
batch.data.source.init=true
12+
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer
13+
batch.database.incrementer.parent=sequenceIncrementerParent
14+
batch.verify.cursor.position=true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
DROP TABLE T_FOOS;
2+
DROP TABLE T_WRITE_FOOS;
3+
4+
CREATE TABLE T_FOOS (
5+
ID BIGINT NOT NULL,
6+
NAME VARCHAR(45),
7+
CODE VARCHAR(10),
8+
VALUE BIGINT
9+
);
10+
11+
ALTER TABLE T_FOOS ADD PRIMARY KEY (ID);
12+
13+
INSERT INTO t_foos (id, name, value) VALUES (1, 'bar2', 2);
14+
INSERT INTO t_foos (id, name, value) VALUES (2, 'bar4', 4);
15+
INSERT INTO t_foos (id, name, value) VALUES (3, 'bar1', 1);
16+
INSERT INTO t_foos (id, name, value) VALUES (4, 'bar5', 5);
17+
INSERT INTO t_foos (id, name, value) VALUES (5, 'bar3', 3);
18+
19+
CREATE TABLE T_WRITE_FOOS (
20+
ID BIGINT NOT NULL,
21+
NAME VARCHAR(45),
22+
VALUE BIGINT
23+
);
24+
25+
ALTER TABLE T_WRITE_FOOS ADD PRIMARY KEY (ID);

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/builder/JdbcPagingItemReaderBuilder.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017 the original author or authors.
2+
* Copyright 2017-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import org.springframework.batch.item.database.support.Db2PagingQueryProvider;
2626
import org.springframework.batch.item.database.support.DerbyPagingQueryProvider;
2727
import org.springframework.batch.item.database.support.H2PagingQueryProvider;
28+
import org.springframework.batch.item.database.support.HanaPagingQueryProvider;
2829
import org.springframework.batch.item.database.support.HsqlPagingQueryProvider;
2930
import org.springframework.batch.item.database.support.MySqlPagingQueryProvider;
3031
import org.springframework.batch.item.database.support.OraclePagingQueryProvider;
@@ -343,6 +344,7 @@ private PagingQueryProvider determineQueryProvider(DataSource dataSource) {
343344
case DB2ZOS:
344345
case DB2AS400: provider = new Db2PagingQueryProvider(); break;
345346
case H2: provider = new H2PagingQueryProvider(); break;
347+
case HANA: provider = new HanaPagingQueryProvider(); break;
346348
case HSQL: provider = new HsqlPagingQueryProvider(); break;
347349
case SQLSERVER: provider = new SqlServerPagingQueryProvider(); break;
348350
case MYSQL: provider = new MySqlPagingQueryProvider(); break;

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DefaultDataFieldMaxValueIncrementerFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
2626
import org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer;
2727
import org.springframework.jdbc.support.incrementer.H2SequenceMaxValueIncrementer;
28+
import org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer;
2829
import org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer;
2930
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
3031
import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer;
@@ -37,6 +38,7 @@
3738
import static org.springframework.batch.support.DatabaseType.DB2ZOS;
3839
import static org.springframework.batch.support.DatabaseType.DERBY;
3940
import static org.springframework.batch.support.DatabaseType.H2;
41+
import static org.springframework.batch.support.DatabaseType.HANA;
4042
import static org.springframework.batch.support.DatabaseType.HSQL;
4143
import static org.springframework.batch.support.DatabaseType.MYSQL;
4244
import static org.springframework.batch.support.DatabaseType.ORACLE;
@@ -98,6 +100,9 @@ else if (databaseType == HSQL) {
98100
else if (databaseType == H2) {
99101
return new H2SequenceMaxValueIncrementer(dataSource, incrementerName);
100102
}
103+
else if (databaseType == HANA) {
104+
return new HanaSequenceMaxValueIncrementer(dataSource, incrementerName);
105+
}
101106
else if (databaseType == MYSQL) {
102107
MySQLMaxValueIncrementer mySQLMaxValueIncrementer = new MySQLMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
103108
mySQLMaxValueIncrementer.setUseNewConnection(true);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2006-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.batch.item.database.support;
18+
19+
import org.springframework.batch.item.database.PagingQueryProvider;
20+
import org.springframework.util.StringUtils;
21+
22+
/**
23+
* SAP HANA implementation of a {@link PagingQueryProvider} using database specific features.
24+
*
25+
* @author Jonathan Bregler
26+
* @since 4.0
27+
*/
28+
public class HanaPagingQueryProvider extends AbstractSqlPagingQueryProvider {
29+
30+
@Override
31+
public String generateFirstPageQuery(int pageSize) {
32+
return SqlPagingQueryUtils.generateLimitSqlQuery(this, false, buildLimitClause(pageSize));
33+
}
34+
35+
@Override
36+
public String generateRemainingPagesQuery(int pageSize) {
37+
if(StringUtils.hasText(getGroupClause())) {
38+
return SqlPagingQueryUtils.generateLimitGroupedSqlQuery(this, true, buildLimitClause(pageSize));
39+
}
40+
else {
41+
return SqlPagingQueryUtils.generateLimitSqlQuery(this, true, buildLimitClause(pageSize));
42+
}
43+
}
44+
45+
private String buildLimitClause(int pageSize) {
46+
return new StringBuilder().append("LIMIT ").append(pageSize).toString();
47+
}
48+
49+
@Override
50+
public String generateJumpToItemQuery(int itemIndex, int pageSize) {
51+
int page = itemIndex / pageSize;
52+
int offset = (page * pageSize) - 1;
53+
offset = offset<0 ? 0 : offset;
54+
String limitClause = new StringBuilder().append("LIMIT 1 OFFSET ").append(offset).toString();
55+
return SqlPagingQueryUtils.generateLimitJumpToQuery(this, limitClause);
56+
}
57+
58+
}

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryProviderFactoryBean.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2012 the original author or authors.
2+
* Copyright 2006-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import static org.springframework.batch.support.DatabaseType.DB2AS400;
2222
import static org.springframework.batch.support.DatabaseType.DERBY;
2323
import static org.springframework.batch.support.DatabaseType.H2;
24+
import static org.springframework.batch.support.DatabaseType.HANA;
2425
import static org.springframework.batch.support.DatabaseType.HSQL;
2526
import static org.springframework.batch.support.DatabaseType.MYSQL;
2627
import static org.springframework.batch.support.DatabaseType.ORACLE;
@@ -78,6 +79,7 @@ public class SqlPagingQueryProviderFactoryBean implements FactoryBean<PagingQuer
7879
providers.put(DERBY,new DerbyPagingQueryProvider());
7980
providers.put(HSQL,new HsqlPagingQueryProvider());
8081
providers.put(H2,new H2PagingQueryProvider());
82+
providers.put(HANA,new HanaPagingQueryProvider());
8183
providers.put(MYSQL,new MySqlPagingQueryProvider());
8284
providers.put(ORACLE,new OraclePagingQueryProvider());
8385
providers.put(POSTGRES,new PostgresPagingQueryProvider());

spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DatabaseType.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2007 the original author or authors.
2+
* Copyright 2006-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -47,7 +47,8 @@ public enum DatabaseType {
4747
POSTGRES("PostgreSQL"),
4848
SYBASE("Sybase"),
4949
H2("H2"),
50-
SQLITE("SQLite");
50+
SQLITE("SQLite"),
51+
HANA("HDB");
5152

5253
private static final Map<String, DatabaseType> nameMap;
5354

spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DefaultDataFieldMaxValueIncrementerFactoryTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.jdbc.support.incrementer.Db2LuwMaxValueIncrementer;
2525
import org.springframework.jdbc.support.incrementer.Db2MainframeMaxValueIncrementer;
2626
import org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer;
27+
import org.springframework.jdbc.support.incrementer.HanaSequenceMaxValueIncrementer;
2728
import org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer;
2829
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
2930
import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer;
@@ -62,6 +63,7 @@ public void testSupportedDatabaseType(){
6263
assertTrue(factory.isSupportedIncrementerType("sqlserver"));
6364
assertTrue(factory.isSupportedIncrementerType("sybase"));
6465
assertTrue(factory.isSupportedIncrementerType("sqlite"));
66+
assertTrue(factory.isSupportedIncrementerType("hana"));
6567
}
6668

6769
public void testUnsupportedDatabaseType(){
@@ -128,5 +130,9 @@ public void testSybase(){
128130
public void testSqlite(){
129131
assertTrue(factory.getIncrementer("sqlite", "NAME") instanceof SqliteMaxValueIncrementer);
130132
}
133+
134+
public void testHana(){
135+
assertTrue(factory.getIncrementer("hana", "NAME") instanceof HanaSequenceMaxValueIncrementer);
136+
}
131137

132138
}

0 commit comments

Comments
 (0)