Skip to content
This repository was archived by the owner on Feb 27, 2023. It is now read-only.

Commit 5b01e46

Browse files
author
Scott Stafford
committed
Merge branch 'dev'
2 parents 45337c1 + d264e4e commit 5b01e46

File tree

22 files changed

+1023
-235
lines changed

22 files changed

+1023
-235
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ out/
1111
*.ipr
1212
*.iws
1313
marklogic-spring-batch.sublime*
14+
output-*.xml

build.gradle

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ buildscript {
77
}
88
dependencies {
99
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3"
10-
classpath "com.marklogic:ml-gradle:2.4.0"
10+
classpath "com.marklogic:ml-gradle:2.5.0"
1111
}
1212
}
1313

1414
plugins {
15-
id "com.marklogic.ml-gradle" version "2.4.0"
15+
id "com.marklogic.ml-gradle" version "2.5.0"
1616
id "java"
1717
id "eclipse"
1818
id "idea"
@@ -63,7 +63,7 @@ subprojects {
6363
compile 'org.slf4j:slf4j-api:1.7.21'
6464
compile "org.springframework.batch:spring-batch-core:$springVersion"
6565
compile "com.marklogic:java-client-api:4.0.0-EA4"
66-
compile "com.marklogic:ml-javaclient-util:2.12.0"
66+
compile "com.marklogic:ml-javaclient-util:2.13.0"
6767

6868
testCompile "org.springframework.batch:spring-batch-test:$springVersion"
6969
testCompile "com.marklogic:ml-junit:2.6.0"
@@ -114,23 +114,23 @@ project(':core') {
114114
dependencies {
115115
compile project(':infrastructure')
116116
// Used by ML Job Repo implementation; depends on DataFieldMaxValueIncrementer
117-
compile "org.springframework:spring-jdbc:4.2.6.RELEASE"
117+
compile "org.springframework:spring-jdbc:4.3.7.RELEASE"
118118

119119
// Used by MarkLogicBatchConfiguration, which depends on Jaxb2Marshaller
120-
compile "org.springframework:spring-oxm:4.2.6.RELEASE"
120+
compile "org.springframework:spring-oxm:4.3.7.RELEASE"
121121

122122
// For deploying the ML Job Repo
123-
compile "com.marklogic:ml-app-deployer:2.3.0"
123+
compile "com.marklogic:ml-app-deployer:2.5.0"
124124

125125
// For the CLI
126126
compile "net.sf.jopt-simple:jopt-simple:5.0.1"
127127

128128
runtime "org.springframework.batch:spring-batch-core:$springVersion"
129129

130130
//com.marklogic.spring.batch.Main extends com.marklogic.client.helper.LoggingObject
131-
runtime "com.marklogic:ml-javaclient-util:2.12.0"
131+
runtime "com.marklogic:ml-javaclient-util:2.13.0"
132132

133-
runtime "org.springframework:spring-jdbc:4.2.6.RELEASE"
133+
runtime "org.springframework:spring-jdbc:4.3.7.RELEASE"
134134

135135
runtime "net.sf.jopt-simple:jopt-simple:5.0.1"
136136
}
@@ -168,6 +168,8 @@ project(":infrastructure") {
168168
exclude(group: 'ch.qos.logback')
169169
}
170170

171+
compile "org.springframework:spring-jdbc:4.3.7.RELEASE"
172+
171173
testCompile project(':test')
172174
}
173175

@@ -180,7 +182,7 @@ project(':test') {
180182
compile "org.springframework.batch:spring-batch-test:$springVersion"
181183
compile "com.marklogic:ml-junit:2.6.0"
182184
// For detecting version of MarkLogic
183-
compile "com.marklogic:ml-app-deployer:2.3.0"
185+
compile "com.marklogic:ml-app-deployer:2.5.0"
184186
}
185187
}
186188

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
group=com.marklogic
2-
version=0.10.1
2+
version=0.11.0
33

44
# Subprojects must override this so that publishing works properly
55
# This is used instead of project.name, which cannot be overridden
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package com.marklogic.spring.batch.item.reader;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.batch.item.ExecutionContext;
6+
import org.springframework.batch.item.ItemStreamException;
7+
import org.springframework.batch.item.database.JdbcCursorItemReader;
8+
import org.springframework.batch.item.support.AbstractItemStreamItemReader;
9+
import org.springframework.dao.DataAccessException;
10+
import org.springframework.jdbc.core.ColumnMapRowMapper;
11+
import org.springframework.jdbc.core.ConnectionCallback;
12+
import org.springframework.jdbc.core.JdbcTemplate;
13+
14+
import javax.sql.DataSource;
15+
import java.sql.Connection;
16+
import java.sql.ResultSet;
17+
import java.sql.SQLException;
18+
import java.util.*;
19+
20+
/**
21+
*
22+
* Spring Batch Reader that first queries for all the table names from the given DataSource, and then reads rows from
23+
* every table.
24+
*
25+
* The excludeTableNames property can be used to exclude certain table names from processing.
26+
*
27+
* The tableQueries property can be used to specify a custom SELECT query for a particular table name. By default,
28+
* "SELECT * FROM (table name)" is used.
29+
*/
30+
public class AllTablesItemReader extends AbstractItemStreamItemReader<Map<String, Object>> {
31+
32+
public final static String DEFAULT_TABLE_NAME_KEY = "_tableName";
33+
34+
protected final Logger logger = LoggerFactory.getLogger(getClass());
35+
36+
private DataSource dataSource;
37+
private List<String> tableNames;
38+
private Map<String, JdbcCursorItemReader> tableReaders;
39+
private int tableNameIndex = 0;
40+
private String tableNameKey = DEFAULT_TABLE_NAME_KEY;
41+
private String databaseVendor = "";
42+
43+
// For ignoring certain table names
44+
private Set<String> excludeTableNames;
45+
46+
// For using a custom SQL query for a given table name
47+
private Map<String, String> tableQueries;
48+
49+
public AllTablesItemReader(DataSource dataSource) {
50+
this.dataSource = dataSource;
51+
}
52+
53+
public AllTablesItemReader(DataSource dataSource, String databaseVendor) {
54+
this.databaseVendor = databaseVendor;
55+
this.dataSource = dataSource;
56+
}
57+
58+
/**
59+
* Use the DataSource to get a list of all the tables. Then, create a JdbcCursorItemReader for every table with
60+
* a SQL query of "SELECT * FROM (table)". Put each of those into a List. Set a RowColumnRowMapper on each
61+
* JdbcCursorItemReader so that every row is read as a ColumnMap.
62+
*/
63+
@Override
64+
public void open(ExecutionContext executionContext) throws ItemStreamException {
65+
tableNames = getTableNames();
66+
tableReaders = new HashMap<>();
67+
for (String tableName : tableNames) {
68+
tableReaders.put(tableName, buildTableReader(tableName, executionContext));
69+
}
70+
}
71+
72+
/**
73+
* Reads a row from the active JdbcCursorItemReader. If that returns null, move on to the next JdbcCursorItemReader.
74+
* If there are no more readers, then this method is all done.
75+
*/
76+
@Override
77+
public Map<String, Object> read() throws Exception {
78+
final String currentTableName = tableNames.get(tableNameIndex);
79+
JdbcCursorItemReader<Map<String, Object>> reader = tableReaders.get(currentTableName);
80+
Map<String, Object> result = reader.read();
81+
if (result != null) {
82+
result.put("_tableName", currentTableName);
83+
return result;
84+
}
85+
86+
if (logger.isInfoEnabled()) {
87+
logger.info("Finished reading rows for query: " + reader.getSql());
88+
}
89+
reader.close();
90+
91+
// Bump up index - if we're at the end of the list, we're all done
92+
tableNameIndex++;
93+
if (tableNameIndex >= tableNames.size()) {
94+
return null;
95+
}
96+
97+
return read();
98+
}
99+
100+
/**
101+
* Register a custom SQL query for selecting data from the given table name. By default, a query of
102+
* "SELECT * FROM (table name)" is used.
103+
*
104+
* @param tableName
105+
* @param sql
106+
*/
107+
public void addTableQuery(String tableName, String sql) {
108+
if (tableQueries == null) {
109+
tableQueries = new HashMap<>();
110+
}
111+
tableQueries.put(tableName, sql);
112+
}
113+
114+
/**
115+
* @return a list of the table names, retrieved via the connection metadata object. The excludeTableNames
116+
* property can be used to ignore certain table names.
117+
*/
118+
protected List<String> getTableNames() {
119+
return new JdbcTemplate(dataSource).execute(new ConnectionCallback<List<String>>() {
120+
@Override
121+
public List<String> doInConnection(Connection con) throws SQLException, DataAccessException {
122+
ResultSet rs = con.getMetaData().getTables(null, null, "%", new String[]{"TABLE"});
123+
List<String> list = new ArrayList<>();
124+
while (rs.next()) {
125+
String name = rs.getString("TABLE_NAME");
126+
if (excludeTableNames == null || !excludeTableNames.contains(name)) {
127+
list.add(name);
128+
}
129+
}
130+
return list;
131+
}
132+
});
133+
}
134+
135+
/**
136+
* @param tableName
137+
* @param executionContext
138+
* @return a JdbcCursorItemReader for the given table name. Override this method to alert the SQL statement that's
139+
* used for a particular table.
140+
*/
141+
protected JdbcCursorItemReader<Map<String, Object>> buildTableReader(String tableName, ExecutionContext executionContext) {
142+
JdbcCursorItemReader<Map<String, Object>> reader = new JdbcCursorItemReader<>();
143+
reader.setDataSource(dataSource);
144+
reader.setRowMapper(new ColumnMapRowMapper());
145+
reader.setSql(getSqlQueryForTable(tableName));
146+
reader.open(executionContext);
147+
return reader;
148+
}
149+
150+
/**
151+
* Uses the tableQueries property to see if there's a custom SQL query for the given table name.
152+
*
153+
* @param tableName
154+
* @return
155+
*/
156+
protected String getSqlQueryForTable(String tableName) {
157+
String sql = null;
158+
if (tableQueries != null) {
159+
sql = tableQueries.get(tableName);
160+
}
161+
if (tableName.contains(" ") && "MICROSOFT".equals(databaseVendor.toUpperCase())) {
162+
tableName = "[" + tableName + "]";
163+
}
164+
return sql != null ? sql : "SELECT * FROM " + tableName;
165+
}
166+
167+
public void setExcludeTableNames(Set<String> excludeTableNames) {
168+
this.excludeTableNames = excludeTableNames;
169+
}
170+
171+
public void setTableQueries(Map<String, String> tableQueries) {
172+
this.tableQueries = tableQueries;
173+
}
174+
175+
public void setTableNameKey(String tableNameKey) {
176+
this.tableNameKey = tableNameKey;
177+
}
178+
179+
public void setDatabaseVendor(String databaseVendor) {
180+
this.databaseVendor = databaseVendor;
181+
}
182+
}

infrastructure/src/main/java/com/marklogic/spring/batch/item/reader/CollectionUrisReader.java

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.marklogic.spring.batch.item.reader;
2+
3+
4+
import com.marklogic.client.DatabaseClient;
5+
import com.marklogic.client.document.DocumentManager;
6+
import com.marklogic.client.document.DocumentPage;
7+
import com.marklogic.client.document.DocumentRecord;
8+
import com.marklogic.client.document.GenericDocumentManager;
9+
import com.marklogic.client.helper.DatabaseClientProvider;
10+
import com.marklogic.client.query.StructuredQueryDefinition;
11+
import org.springframework.batch.item.*;
12+
13+
public class DocumentItemReader implements ItemStreamReader<DocumentRecord> {
14+
15+
private DatabaseClientProvider databaseClientProvider;
16+
private GenericDocumentManager docMgr;
17+
private StructuredQueryDefinition queryDef;
18+
private DocumentPage page;
19+
private long numberOfPages = 0;
20+
private long start = 1L;
21+
22+
public DocumentItemReader(DatabaseClientProvider databaseClientProvider, StructuredQueryDefinition queryDef) {
23+
this.databaseClientProvider = databaseClientProvider;
24+
this.queryDef = queryDef;
25+
}
26+
27+
@Override
28+
public DocumentRecord read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
29+
if (page.hasNext()) {
30+
return page.next();
31+
} else if (page.hasNextPage()) {
32+
page = docMgr.search(queryDef, start);
33+
start += page.getPageSize();
34+
return page.next();
35+
} else {
36+
return null;
37+
}
38+
}
39+
40+
@Override
41+
public void open(ExecutionContext executionContext) throws ItemStreamException {
42+
DatabaseClient databaseClient = databaseClientProvider.getDatabaseClient();
43+
docMgr = databaseClient.newDocumentManager();
44+
docMgr.setMetadataCategories(DocumentManager.Metadata.ALL);
45+
page = docMgr.search(queryDef, start);
46+
start += page.getPageSize();
47+
numberOfPages = page.getTotalPages();
48+
executionContext.put("PAGE_INDEX", page.getPageNumber());
49+
}
50+
51+
@Override
52+
public void update(ExecutionContext executionContext) throws ItemStreamException {
53+
executionContext.put("PAGE_INDEX", page.getPageNumber());
54+
}
55+
56+
@Override
57+
public void close() throws ItemStreamException {
58+
page.close();
59+
}
60+
}
61+

0 commit comments

Comments
 (0)