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 Issue #2515
This commit is contained in:
committed by
Mahmoud Ben Hassine
parent
44dd4f7f56
commit
9388abb589
@@ -25,6 +25,7 @@ import org.springframework.batch.item.database.support.AbstractSqlPagingQueryPro
|
||||
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;
|
||||
@@ -359,6 +360,7 @@ public class JdbcPagingItemReaderBuilder<T> {
|
||||
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;
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.jdbc.support.incrementer.Db2MainframeMaxValueIncremen
|
||||
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;
|
||||
@@ -37,6 +38,7 @@ import static org.springframework.batch.support.DatabaseType.DB2AS400;
|
||||
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;
|
||||
@@ -99,6 +101,9 @@ public class DefaultDataFieldMaxValueIncrementerFactory implements DataFieldMaxV
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
@@ -21,6 +21,7 @@ import static org.springframework.batch.support.DatabaseType.DB2ZOS;
|
||||
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;
|
||||
@@ -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());
|
||||
|
||||
@@ -48,7 +48,8 @@ public enum DatabaseType {
|
||||
POSTGRES("PostgreSQL"),
|
||||
SYBASE("Sybase"),
|
||||
H2("H2"),
|
||||
SQLITE("SQLite");
|
||||
SQLITE("SQLite"),
|
||||
HANA("HDB");
|
||||
|
||||
private static final Map<String, DatabaseType> nameMap;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock;
|
||||
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;
|
||||
@@ -63,6 +64,7 @@ public class DefaultDataFieldMaxValueIncrementerFactoryTests extends TestCase {
|
||||
assertTrue(factory.isSupportedIncrementerType("sqlserver"));
|
||||
assertTrue(factory.isSupportedIncrementerType("sybase"));
|
||||
assertTrue(factory.isSupportedIncrementerType("sqlite"));
|
||||
assertTrue(factory.isSupportedIncrementerType("hana"));
|
||||
}
|
||||
|
||||
public void testUnsupportedDatabaseType(){
|
||||
@@ -129,5 +131,9 @@ public class DefaultDataFieldMaxValueIncrementerFactoryTests extends TestCase {
|
||||
public void testSqlite(){
|
||||
assertTrue(factory.getIncrementer("sqlite", "NAME") instanceof SqliteMaxValueIncrementer);
|
||||
}
|
||||
|
||||
public void testHana(){
|
||||
assertTrue(factory.getIncrementer("hana", "NAME") instanceof HanaSequenceMaxValueIncrementer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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 java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.batch.item.database.Order;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Jonathan Bregler
|
||||
* @since 4.0
|
||||
*/
|
||||
public class HanaPagingQueryProviderTests extends AbstractSqlPagingQueryProviderTests {
|
||||
|
||||
public HanaPagingQueryProviderTests() {
|
||||
pagingQueryProvider = new HanaPagingQueryProvider();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testGenerateFirstPageQuery() {
|
||||
String sql = "SELECT id, name, age FROM foo WHERE bar = 1 ORDER BY id ASC LIMIT 100";
|
||||
String s = pagingQueryProvider.generateFirstPageQuery(pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Test @Override
|
||||
public void testGenerateRemainingPagesQuery() {
|
||||
String sql = "SELECT id, name, age FROM foo WHERE (bar = 1) AND ((id > ?)) ORDER BY id ASC LIMIT 100";
|
||||
String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Test @Override
|
||||
public void testGenerateJumpToItemQuery() {
|
||||
String sql = "SELECT id FROM foo WHERE bar = 1 ORDER BY id ASC LIMIT 1 OFFSET 99";
|
||||
String s = pagingQueryProvider.generateJumpToItemQuery(145, pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Test @Override
|
||||
public void testGenerateJumpToItemQueryForFirstPage() {
|
||||
String sql = "SELECT id FROM foo WHERE bar = 1 ORDER BY id ASC LIMIT 1 OFFSET 0";
|
||||
String s = pagingQueryProvider.generateJumpToItemQuery(45, pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testGenerateFirstPageQueryWithGroupBy() {
|
||||
pagingQueryProvider.setGroupClause("dep");
|
||||
String sql = "SELECT id, name, age FROM foo WHERE bar = 1 GROUP BY dep ORDER BY id ASC LIMIT 100";
|
||||
String s = pagingQueryProvider.generateFirstPageQuery(pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testGenerateRemainingPagesQueryWithGroupBy() {
|
||||
pagingQueryProvider.setGroupClause("dep");
|
||||
String sql = "SELECT * FROM (SELECT id, name, age FROM foo WHERE bar = 1 GROUP BY dep) AS MAIN_QRY WHERE ((id > ?)) ORDER BY id ASC LIMIT 100";
|
||||
String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testGenerateJumpToItemQueryWithGroupBy() {
|
||||
pagingQueryProvider.setGroupClause("dep");
|
||||
String sql = "SELECT id FROM foo WHERE bar = 1 GROUP BY dep ORDER BY id ASC LIMIT 1 OFFSET 99";
|
||||
String s = pagingQueryProvider.generateJumpToItemQuery(145, pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testGenerateJumpToItemQueryForFirstPageWithGroupBy() {
|
||||
pagingQueryProvider.setGroupClause("dep");
|
||||
String sql = "SELECT id FROM foo WHERE bar = 1 GROUP BY dep ORDER BY id ASC LIMIT 1 OFFSET 0";
|
||||
String s = pagingQueryProvider.generateJumpToItemQuery(45, pageSize);
|
||||
assertEquals(sql, s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstPageSqlWithAliases() {
|
||||
Map<String, Order> sorts = new HashMap<>();
|
||||
sorts.put("owner.id", Order.ASCENDING);
|
||||
|
||||
this.pagingQueryProvider = new HanaPagingQueryProvider();
|
||||
this.pagingQueryProvider.setSelectClause("SELECT owner.id as ownerid, first_name, last_name, dog_name ");
|
||||
this.pagingQueryProvider.setFromClause("FROM dog_owner owner INNER JOIN dog ON owner.id = dog.id ");
|
||||
this.pagingQueryProvider.setSortKeys(sorts);
|
||||
|
||||
String firstPage = this.pagingQueryProvider.generateFirstPageQuery(5);
|
||||
String jumpToItemQuery = this.pagingQueryProvider.generateJumpToItemQuery(7, 5);
|
||||
String remainingPagesQuery = this.pagingQueryProvider.generateRemainingPagesQuery(5);
|
||||
|
||||
assertEquals("SELECT owner.id as ownerid, first_name, last_name, dog_name FROM dog_owner owner INNER JOIN dog ON owner.id = dog.id ORDER BY owner.id ASC LIMIT 5", firstPage);
|
||||
assertEquals("SELECT owner.id FROM dog_owner owner INNER JOIN dog ON owner.id = dog.id ORDER BY owner.id ASC LIMIT 1 OFFSET 4", jumpToItemQuery);
|
||||
assertEquals("SELECT owner.id as ownerid, first_name, last_name, dog_name FROM dog_owner owner INNER JOIN dog ON owner.id = dog.id WHERE ((owner.id > ?)) ORDER BY owner.id ASC LIMIT 5", remainingPagesQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFirstPageSqlWithMultipleSortKeys() {
|
||||
return "SELECT id, name, age FROM foo WHERE bar = 1 ORDER BY name ASC, id DESC LIMIT 100";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemainingSqlWithMultipleSortKeys() {
|
||||
return "SELECT id, name, age FROM foo WHERE (bar = 1) AND ((name > ?) OR (name = ? AND id < ?)) ORDER BY name ASC, id DESC LIMIT 100";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJumpToItemQueryWithMultipleSortKeys() {
|
||||
return "SELECT name, id FROM foo WHERE bar = 1 ORDER BY name ASC, id DESC LIMIT 1 OFFSET 99";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJumpToItemQueryForFirstPageWithMultipleSortKeys() {
|
||||
return "SELECT name, id FROM foo WHERE bar = 1 ORDER BY name ASC, id DESC LIMIT 1 OFFSET 0";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2014 the original author or authors.
|
||||
* Copyright 2008-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.
|
||||
@@ -33,6 +33,7 @@ import static org.springframework.batch.support.DatabaseType.POSTGRES;
|
||||
import static org.springframework.batch.support.DatabaseType.SQLITE;
|
||||
import static org.springframework.batch.support.DatabaseType.SQLSERVER;
|
||||
import static org.springframework.batch.support.DatabaseType.SYBASE;
|
||||
import static org.springframework.batch.support.DatabaseType.HANA;
|
||||
import static org.springframework.batch.support.DatabaseType.fromProductName;
|
||||
|
||||
/**
|
||||
@@ -57,6 +58,7 @@ public class DatabaseTypeTests {
|
||||
assertEquals(POSTGRES, fromProductName("PostgreSQL"));
|
||||
assertEquals(SYBASE, fromProductName("Sybase"));
|
||||
assertEquals(SQLITE, fromProductName("SQLite"));
|
||||
assertEquals(HANA, fromProductName("HDB"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@@ -142,6 +144,12 @@ public class DatabaseTypeTests {
|
||||
DataSource ds = DatabaseTypeTestUtils.getMockDataSource("Adaptive Server Enterprise");
|
||||
assertEquals(SYBASE, DatabaseType.fromMetaData(ds));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromMetaDataForHana() throws Exception {
|
||||
DataSource ds = DatabaseTypeTestUtils.getMockDataSource("HDB");
|
||||
assertEquals(HANA, DatabaseType.fromMetaData(ds));
|
||||
}
|
||||
|
||||
@Test(expected=MetaDataAccessException.class)
|
||||
public void testBadMetaData() throws Exception {
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user