From 8eaf136bdf1c5002de3ed84687cf8c3792d1cdcc Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Thu, 3 Aug 2017 22:26:00 -0400 Subject: [PATCH 01/15] Add first revision of sqlDbToHubJob --- .gitignore | 3 + examples/spring-batch/build.gradle | 8 +- examples/spring-batch/settings.gradle | 2 +- .../hub/job/SqlDbToHubJobConfig.java | 59 ++ .../marklogic/hub/job/SqlDbToHubJobTest.java | 45 + .../sample/h2/H2DatabaseConfiguration.java | 30 + ...2MockingApplicationContextInitializer.java | 49 ++ .../src/test/resources/db/sampledata.sql | 817 ++++++++++++++++++ .../src/test/resources/job.properties | 13 + 9 files changed, 1021 insertions(+), 5 deletions(-) create mode 100644 examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java create mode 100644 examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java create mode 100644 examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java create mode 100644 examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java create mode 100644 examples/spring-batch/src/test/resources/db/sampledata.sql create mode 100644 examples/spring-batch/src/test/resources/job.properties diff --git a/.gitignore b/.gitignore index e04eed4cb5..91fdca1fa3 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ node_modules/ .tmp/ *.sublime-* .vscode/ +*.iml +*.ipr +*.iws diff --git a/examples/spring-batch/build.gradle b/examples/spring-batch/build.gradle index 8004e9d312..03304ab466 100644 --- a/examples/spring-batch/build.gradle +++ b/examples/spring-batch/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'application' + id 'idea' id 'net.saliman.properties' version '1.4.6' id 'com.marklogic.ml-data-hub' version '2.0.0-rc.1' } @@ -13,11 +14,10 @@ repositories { dependencies { compile 'com.marklogic:marklogic-data-hub:2.0.0-rc.1' - compile "com.marklogic:marklogic-spring-batch-core:0.7.4" - compile 'com.marklogic:ml-javaclient-util:4.0.alpha4' + compile "com.marklogic:marklogic-spring-batch-core:1.0.1" - testCompile "com.marklogic:marklogic-spring-batch-test:0.7.4" - runtime "com.marklogic:marklogic-spring-batch-core:0.7.2" + testCompile "com.h2database:h2:1.4.193" + testCompile "com.marklogic:marklogic-spring-batch-test:1.0.1" } mainClassName = "com.marklogic.spring.batch.Main" diff --git a/examples/spring-batch/settings.gradle b/examples/spring-batch/settings.gradle index 00345b5a56..2debf74425 100644 --- a/examples/spring-batch/settings.gradle +++ b/examples/spring-batch/settings.gradle @@ -1 +1 @@ -rootProject.name="hubBatch" +rootProject.name="rdbmsToHub" diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java new file mode 100644 index 0000000000..aa27efcac6 --- /dev/null +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java @@ -0,0 +1,59 @@ +package com.marklogic.hub.job; + +import com.marklogic.client.document.DocumentWriteOperation; +import com.marklogic.client.helper.DatabaseClientProvider; +import com.marklogic.spring.batch.columnmap.ColumnMapSerializer; +import com.marklogic.spring.batch.columnmap.DefaultStaxColumnMapSerializer; +import com.marklogic.spring.batch.item.processor.ColumnMapProcessor; +import com.marklogic.spring.batch.item.reader.AllTablesItemReader; +import com.marklogic.spring.batch.item.writer.MarkLogicItemWriter; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; +import org.springframework.batch.core.configuration.annotation.JobScope; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.item.ItemWriter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +import javax.sql.DataSource; +import java.util.Map; + +@EnableBatchProcessing +@Import(value = {com.marklogic.spring.batch.config.MarkLogicBatchConfiguration.class }) +public class SqlDbToHubJobConfig { + + private final String JOB_NAME = "sqlDbToHubJob"; + + @Bean + public Job job(JobBuilderFactory jobBuilderFactory, Step step) { + return jobBuilderFactory.get(JOB_NAME).start(step).build(); + } + + @Bean + @JobScope + public Step step( + StepBuilderFactory stepBuilderFactory, + DataSource dataSource, + DatabaseClientProvider databaseClientProvider, + @Value("#{jobParameters['output_collections']}") String[] collections) { + + AllTablesItemReader reader = new AllTablesItemReader(dataSource); + + // Processor - this is a very basic implementation for converting a column map to an XML string + ColumnMapSerializer serializer = new DefaultStaxColumnMapSerializer(); + ColumnMapProcessor processor = new ColumnMapProcessor(serializer); + + ItemWriter writer = new MarkLogicItemWriter(databaseClientProvider.getDatabaseClient()); + + return stepBuilderFactory.get("step1") + ., DocumentWriteOperation>chunk(10) + .reader(reader) + .processor(processor) + .writer(writer) + .build(); + } + +} diff --git a/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java new file mode 100644 index 0000000000..1c543eed23 --- /dev/null +++ b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java @@ -0,0 +1,45 @@ +package com.marklogic.hub.job; + +import com.marklogic.sample.h2.H2DatabaseConfiguration; +import com.marklogic.spring.batch.test.AbstractJobRunnerTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Date; + +@ContextConfiguration( + classes = {SqlDbToHubJobConfig.class, H2DatabaseConfiguration.class} + // initializers = {H2MockingApplicationContextInitializer.class} +) +@RunWith(SpringJUnit4ClassRunner.class) +public class SqlDbToHubJobTest extends AbstractJobRunnerTest { + + @Autowired + JobLauncherTestUtils jobLauncherTestUtils; + + @Test + public void migrateSampleDatabaseToDataHubTest() { + JobParametersBuilder jpb = new JobParametersBuilder(); + jpb.addString("allTables", "true"); + jpb.addString("date", new Date().toString(), true); + try { + JobExecution execution = jobLauncherTestUtils.launchJob(jpb.toJobParameters()); + assertEquals(BatchStatus.COMPLETED, execution.getStatus()); + } catch (Exception e) { + e.printStackTrace(); + } + + getClientTestHelper().assertCollectionSize("CUSTOMER = 50", "CUSTOMER", 50); + getClientTestHelper().assertCollectionSize("INVOICE = 50", "INVOICE", 50); + getClientTestHelper().assertCollectionSize("ITEM = 650", "ITEM", 650); + getClientTestHelper().assertCollectionSize("PRODUCT = 50", "PRODUCT", 50); + + } +} diff --git a/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java b/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java new file mode 100644 index 0000000000..8b703a5a2a --- /dev/null +++ b/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java @@ -0,0 +1,30 @@ +package com.marklogic.sample.h2; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import javax.sql.DataSource; + +@Configuration +public class H2DatabaseConfiguration { + + @Bean + public DataSource dataSource() { + // no need shutdown, EmbeddedDatabaseFactoryBean will take care of this + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + EmbeddedDatabase dataSource = builder + .setType(EmbeddedDatabaseType.H2) + .addScript("db/sampledata.sql") + .build(); + return dataSource; + } + + @Bean + public JdbcTemplate jdbcTemplate(DataSource dataSource) { + return new JdbcTemplate(dataSource); + } +} diff --git a/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java b/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java new file mode 100644 index 0000000000..42bfedc6d7 --- /dev/null +++ b/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java @@ -0,0 +1,49 @@ +package com.marklogic.sample.h2; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.ResourcePropertySource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.mock.env.MockEnvironment; + +import java.io.IOException; + +public class H2MockingApplicationContextInitializer implements ApplicationContextInitializer { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + public static final String DEFAULT_APPLICATION_PROPERTIES = "job.properties"; + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + MockEnvironment env = new MockEnvironment(); + final MutablePropertySources propertySources = env.getPropertySources(); + //propertySources.addFirst(getApplicationProperties(env)); + env.setProperty("jdbc_driver", "org.h2.Driver"); + env.setProperty("jdbc_username", "sa"); + env.setProperty("jdbc_url", "jdbc:h2:mem:testdb"); + env.setProperty("chunk", "5"); + applicationContext.setEnvironment(env); + + } +/* + protected PropertySource getApplicationProperties(final ConfigurableEnvironment environment) { + try { + final Resource propertiesResource = new FileSystemResource(DEFAULT_APPLICATION_PROPERTIES); + final ResourcePropertySource propertySource = new ResourcePropertySource(propertiesResource); + logger.info("Configured application properties from: {}", propertySource); + return propertySource; + } catch (final IOException ex) { + throw new RuntimeException("Unable to load application properties from default location: " + + DEFAULT_APPLICATION_PROPERTIES, ex); + } + } + */ +} diff --git a/examples/spring-batch/src/test/resources/db/sampledata.sql b/examples/spring-batch/src/test/resources/db/sampledata.sql new file mode 100644 index 0000000000..cdfd7715ac --- /dev/null +++ b/examples/spring-batch/src/test/resources/db/sampledata.sql @@ -0,0 +1,817 @@ +DROP TABLE Item IF EXISTS; +DROP TABLE Invoice IF EXISTS; +DROP TABLE Product IF EXISTS; +DROP TABLE Customer IF EXISTS; + +CREATE TABLE Customer(ID INTEGER PRIMARY KEY,FirstName VARCHAR(20),LastName VARCHAR(30),Street VARCHAR(50),City VARCHAR(25)); +CREATE TABLE Product(ID INTEGER PRIMARY KEY,Name VARCHAR(30),Price DECIMAL); +CREATE TABLE Invoice(ID INTEGER PRIMARY KEY,CustomerID INTEGER,Total DECIMAL, FOREIGN KEY (CustomerId) REFERENCES Customer(ID) ON DELETE CASCADE); +CREATE TABLE Item(InvoiceID INTEGER,Item INTEGER,ProductID INTEGER,Quantity INTEGER,Cost DECIMAL,PRIMARY KEY(InvoiceID,Item), FOREIGN KEY (InvoiceId) REFERENCES Invoice (ID) ON DELETE CASCADE, FOREIGN KEY (ProductId) REFERENCES Product(ID) ON DELETE CASCADE); +COMMIT; + + +INSERT INTO Customer VALUES(0,'Laura','Steel','429 Seventh Av.','Dallas'); +INSERT INTO Product VALUES(0,'Iron Iron',54); +INSERT INTO Customer VALUES(1,'Susanne','King','366 - 20th Ave.','Olten'); +INSERT INTO Product VALUES(1,'Chair Shoe',248); +INSERT INTO Customer VALUES(2,'Anne','Miller','20 Upland Pl.','Lyon'); +INSERT INTO Product VALUES(2,'Telephone Clock',248); +INSERT INTO Customer VALUES(3,'Michael','Clancy','542 Upland Pl.','San Francisco'); +INSERT INTO Product VALUES(3,'Chair Chair',254); +INSERT INTO Customer VALUES(4,'Sylvia','Ringer','365 College Av.','Dallas'); +INSERT INTO Product VALUES(4,'Ice Tea Shoe',128); +INSERT INTO Customer VALUES(5,'Laura','Miller','294 Seventh Av.','Paris'); +INSERT INTO Product VALUES(5,'Clock Clock',236); +INSERT INTO Customer VALUES(6,'Laura','White','506 Upland Pl.','Palo Alto'); +INSERT INTO Product VALUES(6,'Ice Tea Chair',98); +INSERT INTO Customer VALUES(7,'James','Peterson','231 Upland Pl.','San Francisco'); +INSERT INTO Product VALUES(7,'Telephone Shoe',84); +INSERT INTO Customer VALUES(8,'Andrew','Miller','288 - 20th Ave.','Seattle'); +INSERT INTO Product VALUES(8,'Ice Tea Clock',226); +INSERT INTO Customer VALUES(9,'James','Schneider','277 Seventh Av.','Berne'); +INSERT INTO Product VALUES(9,'Clock Telephone',172); +INSERT INTO Customer VALUES(10,'Anne','Fuller','135 Upland Pl.','Dallas'); +INSERT INTO Product VALUES(10,'Telephone Ice Tea',204); +INSERT INTO Customer VALUES(11,'Julia','White','412 Upland Pl.','Chicago'); +INSERT INTO Product VALUES(11,'Telephone Iron',88); +INSERT INTO Customer VALUES(12,'George','Ott','381 Upland Pl.','Palo Alto'); +INSERT INTO Product VALUES(12,'Clock Ice Tea',168); +INSERT INTO Customer VALUES(13,'Laura','Ringer','38 College Av.','New York'); +INSERT INTO Product VALUES(13,'Telephone Clock',180); +INSERT INTO Customer VALUES(14,'Bill','Karsen','53 College Av.','Oslo'); +INSERT INTO Product VALUES(14,'Telephone Iron',124); +INSERT INTO Customer VALUES(15,'Bill','Clancy','319 Upland Pl.','Seattle'); +INSERT INTO Product VALUES(15,'Ice Tea Chair',94); +INSERT INTO Customer VALUES(16,'John','Fuller','195 Seventh Av.','New York'); +INSERT INTO Product VALUES(16,'Ice Tea Shoe',194); +INSERT INTO Customer VALUES(17,'Laura','Ott','443 Seventh Av.','Lyon'); +INSERT INTO Product VALUES(17,'Clock Ice Tea',220); +INSERT INTO Customer VALUES(18,'Sylvia','Fuller','158 - 20th Ave.','Paris'); +INSERT INTO Product VALUES(18,'Chair Clock',172); +INSERT INTO Customer VALUES(19,'Susanne','Heiniger','86 - 20th Ave.','Dallas'); +INSERT INTO Product VALUES(19,'Ice Tea Ice Tea',110); +INSERT INTO Customer VALUES(20,'Janet','Schneider','309 - 20th Ave.','Oslo'); +INSERT INTO Product VALUES(20,'Ice Tea Telephone',200); +INSERT INTO Customer VALUES(21,'Julia','Clancy','18 Seventh Av.','Seattle'); +INSERT INTO Product VALUES(21,'Chair Chair',114); +INSERT INTO Customer VALUES(22,'Bill','Ott','250 - 20th Ave.','Berne'); +INSERT INTO Product VALUES(22,'Iron Iron',66); +INSERT INTO Customer VALUES(23,'Julia','Heiniger','358 College Av.','Boston'); +INSERT INTO Product VALUES(23,'Shoe Chair',76); +INSERT INTO Customer VALUES(24,'James','Sommer','333 Upland Pl.','Olten'); +INSERT INTO Product VALUES(24,'Chair Shoe',72); +INSERT INTO Customer VALUES(25,'Sylvia','Steel','269 College Av.','Paris'); +INSERT INTO Product VALUES(25,'Shoe Shoe',162); +INSERT INTO Customer VALUES(26,'James','Clancy','195 Upland Pl.','Oslo'); +INSERT INTO Product VALUES(26,'Shoe Shoe',252); +INSERT INTO Customer VALUES(27,'Bob','Sommer','509 College Av.','Seattle'); +INSERT INTO Product VALUES(27,'Telephone Iron',230); +INSERT INTO Customer VALUES(28,'Susanne','White','74 - 20th Ave.','Lyon'); +INSERT INTO Product VALUES(28,'Clock Iron',30); +INSERT INTO Customer VALUES(29,'Andrew','Smith','254 College Av.','New York'); +INSERT INTO Product VALUES(29,'Chair Telephone',112); +INSERT INTO Customer VALUES(30,'Bill','Sommer','362 - 20th Ave.','Olten'); +INSERT INTO Product VALUES(30,'Shoe Iron',232); +INSERT INTO Customer VALUES(31,'Bob','Ringer','371 College Av.','Olten'); +INSERT INTO Product VALUES(31,'Ice Tea Telephone',48); +INSERT INTO Customer VALUES(32,'Michael','Ott','339 College Av.','Boston'); +INSERT INTO Product VALUES(32,'Clock Iron',190); +INSERT INTO Customer VALUES(33,'Mary','King','491 College Av.','Oslo'); +INSERT INTO Product VALUES(33,'Iron Chair',182); +INSERT INTO Customer VALUES(34,'Julia','May','33 Upland Pl.','Seattle'); +INSERT INTO Product VALUES(34,'Chair Iron',256); +INSERT INTO Customer VALUES(35,'George','Karsen','412 College Av.','Chicago'); +INSERT INTO Product VALUES(35,'Telephone Shoe',76); +INSERT INTO Customer VALUES(36,'John','Steel','276 Upland Pl.','Dallas'); +INSERT INTO Product VALUES(36,'Ice Tea Iron',32); +INSERT INTO Customer VALUES(37,'Michael','Clancy','19 Seventh Av.','Dallas'); +INSERT INTO Product VALUES(37,'Clock Shoe',94); +INSERT INTO Customer VALUES(38,'Andrew','Heiniger','347 College Av.','Lyon'); +INSERT INTO Product VALUES(38,'Clock Ice Tea',216); +INSERT INTO Customer VALUES(39,'Mary','Karsen','202 College Av.','Chicago'); +INSERT INTO Product VALUES(39,'Ice Tea Shoe',154); +INSERT INTO Customer VALUES(40,'Susanne','Miller','440 - 20th Ave.','Dallas'); +INSERT INTO Product VALUES(40,'Shoe Clock',28); +INSERT INTO Customer VALUES(41,'Bill','King','546 College Av.','New York'); +INSERT INTO Product VALUES(41,'Clock Ice Tea',206); +INSERT INTO Customer VALUES(42,'Robert','Ott','503 Seventh Av.','Oslo'); +INSERT INTO Product VALUES(42,'Iron Chair',198); +INSERT INTO Customer VALUES(43,'Susanne','Smith','2 Upland Pl.','Dallas'); +INSERT INTO Product VALUES(43,'Telephone Clock',94); +INSERT INTO Customer VALUES(44,'Sylvia','Ott','361 College Av.','New York'); +INSERT INTO Product VALUES(44,'Ice Tea Ice Tea',96); +INSERT INTO Customer VALUES(45,'Janet','May','396 Seventh Av.','Oslo'); +INSERT INTO Product VALUES(45,'Iron Ice Tea',180); +INSERT INTO Customer VALUES(46,'Andrew','May','172 Seventh Av.','New York'); +INSERT INTO Product VALUES(46,'Ice Tea Clock',62); +INSERT INTO Customer VALUES(47,'Janet','Fuller','445 Upland Pl.','Dallas'); +INSERT INTO Product VALUES(47,'Ice Tea Iron',178); +INSERT INTO Customer VALUES(48,'Robert','White','549 Seventh Av.','San Francisco'); +INSERT INTO Product VALUES(48,'Clock Clock',210); +INSERT INTO Customer VALUES(49,'George','Fuller','534 - 20th Ave.','Olten'); +INSERT INTO Product VALUES(49,'Iron Iron',22); +INSERT INTO Invoice VALUES(0,0,0.0); +INSERT INTO Invoice VALUES(1,33,0.0); +INSERT INTO Invoice VALUES(2,23,0.0); +INSERT INTO Invoice VALUES(3,21,0.0); +INSERT INTO Invoice VALUES(4,30,0.0); +INSERT INTO Invoice VALUES(5,34,0.0); +INSERT INTO Invoice VALUES(6,19,0.0); +INSERT INTO Invoice VALUES(7,26,0.0); +INSERT INTO Invoice VALUES(8,29,0.0); +INSERT INTO Invoice VALUES(9,38,0.0); +INSERT INTO Invoice VALUES(10,24,0.0); +INSERT INTO Invoice VALUES(11,24,0.0); +INSERT INTO Invoice VALUES(12,23,0.0); +INSERT INTO Invoice VALUES(13,39,0.0); +INSERT INTO Invoice VALUES(14,35,0.0); +INSERT INTO Invoice VALUES(15,39,0.0); +INSERT INTO Invoice VALUES(16,45,0.0); +INSERT INTO Invoice VALUES(17,46,0.0); +INSERT INTO Invoice VALUES(18,4,0.0); +INSERT INTO Invoice VALUES(19,9,0.0); +INSERT INTO Invoice VALUES(20,19,0.0); +INSERT INTO Invoice VALUES(21,8,0.0); +INSERT INTO Invoice VALUES(22,40,0.0); +INSERT INTO Invoice VALUES(23,36,0.0); +INSERT INTO Invoice VALUES(24,15,0.0); +INSERT INTO Invoice VALUES(25,31,0.0); +INSERT INTO Invoice VALUES(26,27,0.0); +INSERT INTO Invoice VALUES(27,24,0.0); +INSERT INTO Invoice VALUES(28,35,0.0); +INSERT INTO Invoice VALUES(29,46,0.0); +INSERT INTO Invoice VALUES(30,13,0.0); +INSERT INTO Invoice VALUES(31,22,0.0); +INSERT INTO Invoice VALUES(32,20,0.0); +INSERT INTO Invoice VALUES(33,40,0.0); +INSERT INTO Invoice VALUES(34,33,0.0); +INSERT INTO Invoice VALUES(35,4,0.0); +INSERT INTO Invoice VALUES(36,42,0.0); +INSERT INTO Invoice VALUES(37,39,0.0); +INSERT INTO Invoice VALUES(38,46,0.0); +INSERT INTO Invoice VALUES(39,5,0.0); +INSERT INTO Invoice VALUES(40,4,0.0); +INSERT INTO Invoice VALUES(41,19,0.0); +INSERT INTO Invoice VALUES(42,38,0.0); +INSERT INTO Invoice VALUES(43,13,0.0); +INSERT INTO Invoice VALUES(44,32,0.0); +INSERT INTO Invoice VALUES(45,42,0.0); +INSERT INTO Invoice VALUES(46,24,0.0); +INSERT INTO Invoice VALUES(47,45,0.0); +INSERT INTO Invoice VALUES(48,22,0.0); +INSERT INTO Invoice VALUES(49,32,0.0); +INSERT INTO Item VALUES(0,12,1,11,1.5); +INSERT INTO Item VALUES(0,11,12,4,1.5); +INSERT INTO Item VALUES(0,10,4,8,1.5); +INSERT INTO Item VALUES(0,9,35,4,1.5); +INSERT INTO Item VALUES(0,8,0,23,1.5); +INSERT INTO Item VALUES(0,7,7,10,1.5); +INSERT INTO Item VALUES(0,6,16,9,1.5); +INSERT INTO Item VALUES(0,5,12,15,1.5); +INSERT INTO Item VALUES(0,4,47,1,1.5); +INSERT INTO Item VALUES(0,3,1,9,1.5); +INSERT INTO Item VALUES(0,2,47,3,1.5); +INSERT INTO Item VALUES(0,1,14,19,1.5); +INSERT INTO Item VALUES(0,0,7,12,1.5); +INSERT INTO Item VALUES(1,6,25,19,1.5); +INSERT INTO Item VALUES(1,5,25,9,1.5); +INSERT INTO Item VALUES(1,4,16,16,1.5); +INSERT INTO Item VALUES(1,3,38,8,1.5); +INSERT INTO Item VALUES(1,2,19,6,1.5); +INSERT INTO Item VALUES(1,1,0,9,1.5); +INSERT INTO Item VALUES(1,0,40,8,1.5); +INSERT INTO Item VALUES(2,16,36,24,1.5); +INSERT INTO Item VALUES(2,15,0,18,1.5); +INSERT INTO Item VALUES(2,14,48,19,1.5); +INSERT INTO Item VALUES(2,13,12,1,1.5); +INSERT INTO Item VALUES(2,12,21,15,1.5); +INSERT INTO Item VALUES(2,11,42,21,1.5); +INSERT INTO Item VALUES(2,10,49,11,1.5); +INSERT INTO Item VALUES(2,9,18,7,1.5); +INSERT INTO Item VALUES(2,8,39,2,1.5); +INSERT INTO Item VALUES(2,7,30,5,1.5); +INSERT INTO Item VALUES(2,6,43,8,1.5); +INSERT INTO Item VALUES(2,5,30,4,1.5); +INSERT INTO Item VALUES(2,4,38,18,1.5); +INSERT INTO Item VALUES(2,3,19,13,1.5); +INSERT INTO Item VALUES(2,2,11,9,1.5); +INSERT INTO Item VALUES(2,1,25,3,1.5); +INSERT INTO Item VALUES(2,0,4,18,1.5); +INSERT INTO Item VALUES(3,17,30,17,1.5); +INSERT INTO Item VALUES(3,16,19,11,1.5); +INSERT INTO Item VALUES(3,15,23,18,1.5); +INSERT INTO Item VALUES(3,14,17,22,1.5); +INSERT INTO Item VALUES(3,13,41,2,1.5); +INSERT INTO Item VALUES(3,12,41,22,1.5); +INSERT INTO Item VALUES(3,11,7,11,1.5); +INSERT INTO Item VALUES(3,10,10,17,1.5); +INSERT INTO Item VALUES(3,9,29,17,1.5); +INSERT INTO Item VALUES(3,8,49,9,1.5); +INSERT INTO Item VALUES(3,7,26,4,1.5); +INSERT INTO Item VALUES(3,6,13,18,1.5); +INSERT INTO Item VALUES(3,5,30,10,1.5); +INSERT INTO Item VALUES(3,4,20,12,1.5); +INSERT INTO Item VALUES(3,3,0,22,1.5); +INSERT INTO Item VALUES(3,2,49,3,1.5); +INSERT INTO Item VALUES(3,1,1,20,1.5); +INSERT INTO Item VALUES(3,0,11,21,1.5); +INSERT INTO Item VALUES(4,5,37,24,1.5); +INSERT INTO Item VALUES(4,4,9,18,1.5); +INSERT INTO Item VALUES(4,3,23,20,1.5); +INSERT INTO Item VALUES(4,2,41,23,1.5); +INSERT INTO Item VALUES(4,1,35,15,1.5); +INSERT INTO Item VALUES(4,0,28,9,1.5); +INSERT INTO Item VALUES(5,13,18,17,1.5); +INSERT INTO Item VALUES(5,12,8,15,1.5); +INSERT INTO Item VALUES(5,11,38,23,1.5); +INSERT INTO Item VALUES(5,10,28,18,1.5); +INSERT INTO Item VALUES(5,9,37,9,1.5); +INSERT INTO Item VALUES(5,8,20,3,1.5); +INSERT INTO Item VALUES(5,7,2,4,1.5); +INSERT INTO Item VALUES(5,6,7,9,1.5); +INSERT INTO Item VALUES(5,5,46,15,1.5); +INSERT INTO Item VALUES(5,4,32,14,1.5); +INSERT INTO Item VALUES(5,3,24,12,1.5); +INSERT INTO Item VALUES(5,2,20,18,1.5); +INSERT INTO Item VALUES(5,1,9,23,1.5); +INSERT INTO Item VALUES(5,0,9,5,1.5); +INSERT INTO Item VALUES(6,11,44,1,1.5); +INSERT INTO Item VALUES(6,10,16,13,1.5); +INSERT INTO Item VALUES(6,9,19,2,1.5); +INSERT INTO Item VALUES(6,8,41,19,1.5); +INSERT INTO Item VALUES(6,7,26,10,1.5); +INSERT INTO Item VALUES(6,6,37,22,1.5); +INSERT INTO Item VALUES(6,5,14,20,1.5); +INSERT INTO Item VALUES(6,4,31,20,1.5); +INSERT INTO Item VALUES(6,3,30,2,1.5); +INSERT INTO Item VALUES(6,2,23,8,1.5); +INSERT INTO Item VALUES(6,1,38,21,1.5); +INSERT INTO Item VALUES(6,0,15,20,1.5); +INSERT INTO Item VALUES(7,18,23,5,1.5); +INSERT INTO Item VALUES(7,17,40,1,1.5); +INSERT INTO Item VALUES(7,16,7,12,1.5); +INSERT INTO Item VALUES(7,15,24,17,1.5); +INSERT INTO Item VALUES(7,14,47,14,1.5); +INSERT INTO Item VALUES(7,13,32,23,1.5); +INSERT INTO Item VALUES(7,12,40,16,1.5); +INSERT INTO Item VALUES(7,11,19,13,1.5); +INSERT INTO Item VALUES(7,10,7,1,1.5); +INSERT INTO Item VALUES(7,9,9,21,1.5); +INSERT INTO Item VALUES(7,8,42,19,1.5); +INSERT INTO Item VALUES(7,7,2,22,1.5); +INSERT INTO Item VALUES(7,6,14,4,1.5); +INSERT INTO Item VALUES(7,5,24,10,1.5); +INSERT INTO Item VALUES(7,4,2,13,1.5); +INSERT INTO Item VALUES(7,3,30,2,1.5); +INSERT INTO Item VALUES(7,2,27,17,1.5); +INSERT INTO Item VALUES(7,1,23,12,1.5); +INSERT INTO Item VALUES(7,0,43,16,1.5); +INSERT INTO Item VALUES(8,9,21,23,1.5); +INSERT INTO Item VALUES(8,8,38,7,1.5); +INSERT INTO Item VALUES(8,7,6,5,1.5); +INSERT INTO Item VALUES(8,6,15,19,1.5); +INSERT INTO Item VALUES(8,5,24,18,1.5); +INSERT INTO Item VALUES(8,4,15,8,1.5); +INSERT INTO Item VALUES(8,3,41,16,1.5); +INSERT INTO Item VALUES(8,2,11,8,1.5); +INSERT INTO Item VALUES(8,1,44,16,1.5); +INSERT INTO Item VALUES(8,0,34,15,1.5); +INSERT INTO Item VALUES(9,19,48,23,1.5); +INSERT INTO Item VALUES(9,18,3,12,1.5); +INSERT INTO Item VALUES(9,17,13,17,1.5); +INSERT INTO Item VALUES(9,16,24,10,1.5); +INSERT INTO Item VALUES(9,15,48,23,1.5); +INSERT INTO Item VALUES(9,14,15,8,1.5); +INSERT INTO Item VALUES(9,13,42,13,1.5); +INSERT INTO Item VALUES(9,12,25,23,1.5); +INSERT INTO Item VALUES(9,11,12,16,1.5); +INSERT INTO Item VALUES(9,10,38,11,1.5); +INSERT INTO Item VALUES(9,9,13,6,1.5); +INSERT INTO Item VALUES(9,8,24,11,1.5); +INSERT INTO Item VALUES(9,7,2,22,1.5); +INSERT INTO Item VALUES(9,6,18,10,1.5); +INSERT INTO Item VALUES(9,5,6,2,1.5); +INSERT INTO Item VALUES(9,4,36,16,1.5); +INSERT INTO Item VALUES(9,3,4,14,1.5); +INSERT INTO Item VALUES(9,2,29,12,1.5); +INSERT INTO Item VALUES(9,1,18,21,1.5); +INSERT INTO Item VALUES(9,0,45,8,1.5); +INSERT INTO Item VALUES(10,9,5,10,1.5); +INSERT INTO Item VALUES(10,8,22,11,1.5); +INSERT INTO Item VALUES(10,7,4,13,1.5); +INSERT INTO Item VALUES(10,6,18,14,1.5); +INSERT INTO Item VALUES(10,5,5,24,1.5); +INSERT INTO Item VALUES(10,4,10,24,1.5); +INSERT INTO Item VALUES(10,3,46,1,1.5); +INSERT INTO Item VALUES(10,2,7,9,1.5); +INSERT INTO Item VALUES(10,1,33,17,1.5); +INSERT INTO Item VALUES(10,0,20,1,1.5); +INSERT INTO Item VALUES(11,8,20,1,1.5); +INSERT INTO Item VALUES(11,7,48,22,1.5); +INSERT INTO Item VALUES(11,6,0,12,1.5); +INSERT INTO Item VALUES(11,5,19,2,1.5); +INSERT INTO Item VALUES(11,4,47,16,1.5); +INSERT INTO Item VALUES(11,3,32,21,1.5); +INSERT INTO Item VALUES(11,2,0,3,1.5); +INSERT INTO Item VALUES(11,1,21,21,1.5); +INSERT INTO Item VALUES(11,0,45,10,1.5); +INSERT INTO Item VALUES(12,18,9,4,1.5); +INSERT INTO Item VALUES(12,17,31,15,1.5); +INSERT INTO Item VALUES(12,16,0,9,1.5); +INSERT INTO Item VALUES(12,15,22,16,1.5); +INSERT INTO Item VALUES(12,14,25,11,1.5); +INSERT INTO Item VALUES(12,13,36,21,1.5); +INSERT INTO Item VALUES(12,12,13,12,1.5); +INSERT INTO Item VALUES(12,11,28,16,1.5); +INSERT INTO Item VALUES(12,10,46,19,1.5); +INSERT INTO Item VALUES(12,9,25,22,1.5); +INSERT INTO Item VALUES(12,8,48,2,1.5); +INSERT INTO Item VALUES(12,7,48,7,1.5); +INSERT INTO Item VALUES(12,6,31,15,1.5); +INSERT INTO Item VALUES(12,5,37,17,1.5); +INSERT INTO Item VALUES(12,4,20,11,1.5); +INSERT INTO Item VALUES(12,3,0,18,1.5); +INSERT INTO Item VALUES(12,2,6,5,1.5); +INSERT INTO Item VALUES(12,1,41,19,1.5); +INSERT INTO Item VALUES(12,0,1,24,1.5); +INSERT INTO Item VALUES(13,21,40,1,1.5); +INSERT INTO Item VALUES(13,20,5,19,1.5); +INSERT INTO Item VALUES(13,19,42,18,1.5); +INSERT INTO Item VALUES(13,18,0,16,1.5); +INSERT INTO Item VALUES(13,17,32,18,1.5); +INSERT INTO Item VALUES(13,16,22,23,1.5); +INSERT INTO Item VALUES(13,15,0,20,1.5); +INSERT INTO Item VALUES(13,14,1,12,1.5); +INSERT INTO Item VALUES(13,13,10,20,1.5); +INSERT INTO Item VALUES(13,12,17,3,1.5); +INSERT INTO Item VALUES(13,11,14,3,1.5); +INSERT INTO Item VALUES(13,10,45,24,1.5); +INSERT INTO Item VALUES(13,9,24,10,1.5); +INSERT INTO Item VALUES(13,8,48,11,1.5); +INSERT INTO Item VALUES(13,7,29,24,1.5); +INSERT INTO Item VALUES(13,6,19,8,1.5); +INSERT INTO Item VALUES(13,5,22,19,1.5); +INSERT INTO Item VALUES(13,4,26,21,1.5); +INSERT INTO Item VALUES(13,3,32,2,1.5); +INSERT INTO Item VALUES(13,2,13,20,1.5); +INSERT INTO Item VALUES(13,1,1,1,1.5); +INSERT INTO Item VALUES(13,0,16,10,1.5); +INSERT INTO Item VALUES(14,13,11,23,1.5); +INSERT INTO Item VALUES(14,12,4,20,1.5); +INSERT INTO Item VALUES(14,11,25,15,1.5); +INSERT INTO Item VALUES(14,10,44,16,1.5); +INSERT INTO Item VALUES(14,9,13,16,1.5); +INSERT INTO Item VALUES(14,8,23,7,1.5); +INSERT INTO Item VALUES(14,7,43,4,1.5); +INSERT INTO Item VALUES(14,6,26,18,1.5); +INSERT INTO Item VALUES(14,5,11,8,1.5); +INSERT INTO Item VALUES(14,4,41,17,1.5); +INSERT INTO Item VALUES(14,3,34,11,1.5); +INSERT INTO Item VALUES(14,2,15,18,1.5); +INSERT INTO Item VALUES(14,1,9,22,1.5); +INSERT INTO Item VALUES(14,0,42,18,1.5); +INSERT INTO Item VALUES(15,2,24,6,1.5); +INSERT INTO Item VALUES(15,1,13,21,1.5); +INSERT INTO Item VALUES(15,0,17,12,1.5); +INSERT INTO Item VALUES(16,15,12,3,1.5); +INSERT INTO Item VALUES(16,14,0,19,1.5); +INSERT INTO Item VALUES(16,13,20,1,1.5); +INSERT INTO Item VALUES(16,12,18,2,1.5); +INSERT INTO Item VALUES(16,11,24,7,1.5); +INSERT INTO Item VALUES(16,10,43,8,1.5); +INSERT INTO Item VALUES(16,9,11,10,1.5); +INSERT INTO Item VALUES(16,8,13,17,1.5); +INSERT INTO Item VALUES(16,7,8,17,1.5); +INSERT INTO Item VALUES(16,6,44,7,1.5); +INSERT INTO Item VALUES(16,5,11,15,1.5); +INSERT INTO Item VALUES(16,4,10,24,1.5); +INSERT INTO Item VALUES(16,3,0,3,1.5); +INSERT INTO Item VALUES(16,2,20,15,1.5); +INSERT INTO Item VALUES(16,1,36,20,1.5); +INSERT INTO Item VALUES(16,0,18,15,1.5); +INSERT INTO Item VALUES(17,19,46,12,1.5); +INSERT INTO Item VALUES(17,18,5,9,1.5); +INSERT INTO Item VALUES(17,17,7,5,1.5); +INSERT INTO Item VALUES(17,16,8,16,1.5); +INSERT INTO Item VALUES(17,15,35,10,1.5); +INSERT INTO Item VALUES(17,14,18,2,1.5); +INSERT INTO Item VALUES(17,13,41,5,1.5); +INSERT INTO Item VALUES(17,12,22,16,1.5); +INSERT INTO Item VALUES(17,11,45,10,1.5); +INSERT INTO Item VALUES(17,10,10,12,1.5); +INSERT INTO Item VALUES(17,9,8,15,1.5); +INSERT INTO Item VALUES(17,8,49,8,1.5); +INSERT INTO Item VALUES(17,7,6,15,1.5); +INSERT INTO Item VALUES(17,6,43,6,1.5); +INSERT INTO Item VALUES(17,5,44,1,1.5); +INSERT INTO Item VALUES(17,4,23,2,1.5); +INSERT INTO Item VALUES(17,3,24,4,1.5); +INSERT INTO Item VALUES(17,2,44,11,1.5); +INSERT INTO Item VALUES(17,1,19,19,1.5); +INSERT INTO Item VALUES(17,0,16,8,1.5); +INSERT INTO Item VALUES(18,18,10,1,1.5); +INSERT INTO Item VALUES(18,17,8,1,1.5); +INSERT INTO Item VALUES(18,16,31,12,1.5); +INSERT INTO Item VALUES(18,15,44,20,1.5); +INSERT INTO Item VALUES(18,14,28,20,1.5); +INSERT INTO Item VALUES(18,13,14,12,1.5); +INSERT INTO Item VALUES(18,12,37,12,1.5); +INSERT INTO Item VALUES(18,11,30,8,1.5); +INSERT INTO Item VALUES(18,10,34,18,1.5); +INSERT INTO Item VALUES(18,9,2,2,1.5); +INSERT INTO Item VALUES(18,8,1,24,1.5); +INSERT INTO Item VALUES(18,7,15,14,1.5); +INSERT INTO Item VALUES(18,6,29,4,1.5); +INSERT INTO Item VALUES(18,5,15,6,1.5); +INSERT INTO Item VALUES(18,4,28,6,1.5); +INSERT INTO Item VALUES(18,3,19,8,1.5); +INSERT INTO Item VALUES(18,2,40,12,1.5); +INSERT INTO Item VALUES(18,1,33,12,1.5); +INSERT INTO Item VALUES(18,0,32,1,1.5); +INSERT INTO Item VALUES(19,4,36,24,1.5); +INSERT INTO Item VALUES(19,3,49,23,1.5); +INSERT INTO Item VALUES(19,2,4,22,1.5); +INSERT INTO Item VALUES(19,1,31,2,1.5); +INSERT INTO Item VALUES(19,0,12,7,1.5); +INSERT INTO Item VALUES(20,11,15,8,1.5); +INSERT INTO Item VALUES(20,10,25,11,1.5); +INSERT INTO Item VALUES(20,9,12,8,1.5); +INSERT INTO Item VALUES(20,8,44,18,1.5); +INSERT INTO Item VALUES(20,7,9,9,1.5); +INSERT INTO Item VALUES(20,6,20,2,1.5); +INSERT INTO Item VALUES(20,5,8,14,1.5); +INSERT INTO Item VALUES(20,4,30,13,1.5); +INSERT INTO Item VALUES(20,3,25,14,1.5); +INSERT INTO Item VALUES(20,2,24,22,1.5); +INSERT INTO Item VALUES(20,1,29,6,1.5); +INSERT INTO Item VALUES(20,0,47,15,1.5); +INSERT INTO Item VALUES(21,11,20,11,1.5); +INSERT INTO Item VALUES(21,10,19,14,1.5); +INSERT INTO Item VALUES(21,9,35,17,1.5); +INSERT INTO Item VALUES(21,8,44,19,1.5); +INSERT INTO Item VALUES(21,7,8,9,1.5); +INSERT INTO Item VALUES(21,6,26,7,1.5); +INSERT INTO Item VALUES(21,5,27,18,1.5); +INSERT INTO Item VALUES(21,4,49,22,1.5); +INSERT INTO Item VALUES(21,3,30,13,1.5); +INSERT INTO Item VALUES(21,2,31,17,1.5); +INSERT INTO Item VALUES(21,1,38,19,1.5); +INSERT INTO Item VALUES(21,0,9,10,1.5); +INSERT INTO Item VALUES(22,9,23,1,1.5); +INSERT INTO Item VALUES(22,8,3,2,1.5); +INSERT INTO Item VALUES(22,7,21,6,1.5); +INSERT INTO Item VALUES(22,6,4,11,1.5); +INSERT INTO Item VALUES(22,5,24,5,1.5); +INSERT INTO Item VALUES(22,4,5,21,1.5); +INSERT INTO Item VALUES(22,3,22,5,1.5); +INSERT INTO Item VALUES(22,2,12,20,1.5); +INSERT INTO Item VALUES(22,1,30,11,1.5); +INSERT INTO Item VALUES(22,0,9,6,1.5); +INSERT INTO Item VALUES(23,16,8,11,1.5); +INSERT INTO Item VALUES(23,15,13,17,1.5); +INSERT INTO Item VALUES(23,14,44,2,1.5); +INSERT INTO Item VALUES(23,13,14,17,1.5); +INSERT INTO Item VALUES(23,12,4,17,1.5); +INSERT INTO Item VALUES(23,11,41,8,1.5); +INSERT INTO Item VALUES(23,10,4,18,1.5); +INSERT INTO Item VALUES(23,9,20,18,1.5); +INSERT INTO Item VALUES(23,8,6,17,1.5); +INSERT INTO Item VALUES(23,7,39,3,1.5); +INSERT INTO Item VALUES(23,6,16,1,1.5); +INSERT INTO Item VALUES(23,5,32,14,1.5); +INSERT INTO Item VALUES(23,4,23,19,1.5); +INSERT INTO Item VALUES(23,3,40,19,1.5); +INSERT INTO Item VALUES(23,2,33,18,1.5); +INSERT INTO Item VALUES(23,1,26,8,1.5); +INSERT INTO Item VALUES(23,0,48,22,1.5); +INSERT INTO Item VALUES(24,15,39,17,1.5); +INSERT INTO Item VALUES(24,14,1,13,1.5); +INSERT INTO Item VALUES(24,13,15,21,1.5); +INSERT INTO Item VALUES(24,12,0,8,1.5); +INSERT INTO Item VALUES(24,11,1,4,1.5); +INSERT INTO Item VALUES(24,10,27,4,1.5); +INSERT INTO Item VALUES(24,9,21,8,1.5); +INSERT INTO Item VALUES(24,8,5,18,1.5); +INSERT INTO Item VALUES(24,7,7,13,1.5); +INSERT INTO Item VALUES(24,6,40,3,1.5); +INSERT INTO Item VALUES(24,5,35,16,1.5); +INSERT INTO Item VALUES(24,4,15,17,1.5); +INSERT INTO Item VALUES(24,3,17,23,1.5); +INSERT INTO Item VALUES(24,2,38,10,1.5); +INSERT INTO Item VALUES(24,1,46,18,1.5); +INSERT INTO Item VALUES(24,0,43,14,1.5); +INSERT INTO Item VALUES(25,8,38,3,1.5); +INSERT INTO Item VALUES(25,7,16,8,1.5); +INSERT INTO Item VALUES(25,6,21,18,1.5); +INSERT INTO Item VALUES(25,5,10,5,1.5); +INSERT INTO Item VALUES(25,4,47,10,1.5); +INSERT INTO Item VALUES(25,3,19,4,1.5); +INSERT INTO Item VALUES(25,2,13,8,1.5); +INSERT INTO Item VALUES(25,1,43,13,1.5); +INSERT INTO Item VALUES(25,0,5,15,1.5); +INSERT INTO Item VALUES(26,16,30,4,1.5); +INSERT INTO Item VALUES(26,15,8,6,1.5); +INSERT INTO Item VALUES(26,14,26,6,1.5); +INSERT INTO Item VALUES(26,13,13,10,1.5); +INSERT INTO Item VALUES(26,12,27,20,1.5); +INSERT INTO Item VALUES(26,11,18,3,1.5); +INSERT INTO Item VALUES(26,10,34,16,1.5); +INSERT INTO Item VALUES(26,9,1,23,1.5); +INSERT INTO Item VALUES(26,8,40,13,1.5); +INSERT INTO Item VALUES(26,7,4,16,1.5); +INSERT INTO Item VALUES(26,6,7,23,1.5); +INSERT INTO Item VALUES(26,5,38,4,1.5); +INSERT INTO Item VALUES(26,4,46,7,1.5); +INSERT INTO Item VALUES(26,3,16,3,1.5); +INSERT INTO Item VALUES(26,2,33,7,1.5); +INSERT INTO Item VALUES(26,1,43,21,1.5); +INSERT INTO Item VALUES(26,0,42,16,1.5); +INSERT INTO Item VALUES(27,2,19,1,1.5); +INSERT INTO Item VALUES(27,1,45,15,1.5); +INSERT INTO Item VALUES(27,0,24,15,1.5); +INSERT INTO Item VALUES(28,8,28,6,1.5); +INSERT INTO Item VALUES(28,7,28,8,1.5); +INSERT INTO Item VALUES(28,6,33,16,1.5); +INSERT INTO Item VALUES(28,5,49,4,1.5); +INSERT INTO Item VALUES(28,4,45,17,1.5); +INSERT INTO Item VALUES(28,3,6,3,1.5); +INSERT INTO Item VALUES(28,2,44,22,1.5); +INSERT INTO Item VALUES(28,1,15,13,1.5); +INSERT INTO Item VALUES(28,0,35,13,1.5); +INSERT INTO Item VALUES(29,8,35,6,1.5); +INSERT INTO Item VALUES(29,7,5,1,1.5); +INSERT INTO Item VALUES(29,6,4,16,1.5); +INSERT INTO Item VALUES(29,5,31,13,1.5); +INSERT INTO Item VALUES(29,4,4,7,1.5); +INSERT INTO Item VALUES(29,3,7,21,1.5); +INSERT INTO Item VALUES(29,2,17,23,1.5); +INSERT INTO Item VALUES(29,1,38,12,1.5); +INSERT INTO Item VALUES(29,0,33,17,1.5); +INSERT INTO Item VALUES(30,6,14,23,1.5); +INSERT INTO Item VALUES(30,5,43,23,1.5); +INSERT INTO Item VALUES(30,4,34,2,1.5); +INSERT INTO Item VALUES(30,3,33,2,1.5); +INSERT INTO Item VALUES(30,2,10,18,1.5); +INSERT INTO Item VALUES(30,1,16,19,1.5); +INSERT INTO Item VALUES(30,0,14,7,1.5); +INSERT INTO Item VALUES(31,10,0,3,1.5); +INSERT INTO Item VALUES(31,9,14,15,1.5); +INSERT INTO Item VALUES(31,8,7,5,1.5); +INSERT INTO Item VALUES(31,7,38,3,1.5); +INSERT INTO Item VALUES(31,6,26,16,1.5); +INSERT INTO Item VALUES(31,5,1,4,1.5); +INSERT INTO Item VALUES(31,4,8,14,1.5); +INSERT INTO Item VALUES(31,3,12,10,1.5); +INSERT INTO Item VALUES(31,2,4,3,1.5); +INSERT INTO Item VALUES(31,1,4,23,1.5); +INSERT INTO Item VALUES(31,0,33,10,1.5); +INSERT INTO Item VALUES(32,2,1,14,1.5); +INSERT INTO Item VALUES(32,1,30,13,1.5); +INSERT INTO Item VALUES(32,0,35,11,1.5); +INSERT INTO Item VALUES(33,15,38,7,1.5); +INSERT INTO Item VALUES(33,14,44,13,1.5); +INSERT INTO Item VALUES(33,13,25,16,1.5); +INSERT INTO Item VALUES(33,12,16,23,1.5); +INSERT INTO Item VALUES(33,11,5,7,1.5); +INSERT INTO Item VALUES(33,10,24,9,1.5); +INSERT INTO Item VALUES(33,9,29,5,1.5); +INSERT INTO Item VALUES(33,8,3,15,1.5); +INSERT INTO Item VALUES(33,7,43,10,1.5); +INSERT INTO Item VALUES(33,6,17,16,1.5); +INSERT INTO Item VALUES(33,5,8,11,1.5); +INSERT INTO Item VALUES(33,4,24,1,1.5); +INSERT INTO Item VALUES(33,3,48,1,1.5); +INSERT INTO Item VALUES(33,2,36,16,1.5); +INSERT INTO Item VALUES(33,1,10,21,1.5); +INSERT INTO Item VALUES(33,0,36,5,1.5); +INSERT INTO Item VALUES(34,14,46,7,1.5); +INSERT INTO Item VALUES(34,13,30,14,1.5); +INSERT INTO Item VALUES(34,12,43,21,1.5); +INSERT INTO Item VALUES(34,11,4,17,1.5); +INSERT INTO Item VALUES(34,10,41,16,1.5); +INSERT INTO Item VALUES(34,9,8,17,1.5); +INSERT INTO Item VALUES(34,8,3,1,1.5); +INSERT INTO Item VALUES(34,7,21,22,1.5); +INSERT INTO Item VALUES(34,6,32,7,1.5); +INSERT INTO Item VALUES(34,5,45,13,1.5); +INSERT INTO Item VALUES(34,4,27,1,1.5); +INSERT INTO Item VALUES(34,3,44,15,1.5); +INSERT INTO Item VALUES(34,2,28,22,1.5); +INSERT INTO Item VALUES(34,1,4,3,1.5); +INSERT INTO Item VALUES(34,0,10,22,1.5); +INSERT INTO Item VALUES(35,13,19,17,1.5); +INSERT INTO Item VALUES(35,12,7,23,1.5); +INSERT INTO Item VALUES(35,11,44,9,1.5); +INSERT INTO Item VALUES(35,10,17,11,1.5); +INSERT INTO Item VALUES(35,9,19,1,1.5); +INSERT INTO Item VALUES(35,8,0,1,1.5); +INSERT INTO Item VALUES(35,7,22,15,1.5); +INSERT INTO Item VALUES(35,6,5,4,1.5); +INSERT INTO Item VALUES(35,5,33,5,1.5); +INSERT INTO Item VALUES(35,4,14,17,1.5); +INSERT INTO Item VALUES(35,3,27,10,1.5); +INSERT INTO Item VALUES(35,2,14,4,1.5); +INSERT INTO Item VALUES(35,1,3,9,1.5); +INSERT INTO Item VALUES(35,0,20,17,1.5); +INSERT INTO Item VALUES(36,11,44,9,1.5); +INSERT INTO Item VALUES(36,10,47,11,1.5); +INSERT INTO Item VALUES(36,9,31,18,1.5); +INSERT INTO Item VALUES(36,8,4,21,1.5); +INSERT INTO Item VALUES(36,7,39,19,1.5); +INSERT INTO Item VALUES(36,6,39,20,1.5); +INSERT INTO Item VALUES(36,5,25,8,1.5); +INSERT INTO Item VALUES(36,4,40,5,1.5); +INSERT INTO Item VALUES(36,3,10,8,1.5); +INSERT INTO Item VALUES(36,2,1,6,1.5); +INSERT INTO Item VALUES(36,1,15,23,1.5); +INSERT INTO Item VALUES(36,0,18,13,1.5); +INSERT INTO Item VALUES(37,21,6,9,1.5); +INSERT INTO Item VALUES(37,20,14,1,1.5); +INSERT INTO Item VALUES(37,19,19,20,1.5); +INSERT INTO Item VALUES(37,18,26,22,1.5); +INSERT INTO Item VALUES(37,17,38,18,1.5); +INSERT INTO Item VALUES(37,16,27,8,1.5); +INSERT INTO Item VALUES(37,15,32,12,1.5); +INSERT INTO Item VALUES(37,14,12,3,1.5); +INSERT INTO Item VALUES(37,13,32,3,1.5); +INSERT INTO Item VALUES(37,12,24,23,1.5); +INSERT INTO Item VALUES(37,11,30,5,1.5); +INSERT INTO Item VALUES(37,10,1,18,1.5); +INSERT INTO Item VALUES(37,9,47,16,1.5); +INSERT INTO Item VALUES(37,8,46,9,1.5); +INSERT INTO Item VALUES(37,7,24,19,1.5); +INSERT INTO Item VALUES(37,6,34,12,1.5); +INSERT INTO Item VALUES(37,5,1,14,1.5); +INSERT INTO Item VALUES(37,4,13,20,1.5); +INSERT INTO Item VALUES(37,3,26,7,1.5); +INSERT INTO Item VALUES(37,2,36,8,1.5); +INSERT INTO Item VALUES(37,1,15,20,1.5); +INSERT INTO Item VALUES(37,0,41,24,1.5); +INSERT INTO Item VALUES(38,19,4,7,1.5); +INSERT INTO Item VALUES(38,18,28,20,1.5); +INSERT INTO Item VALUES(38,17,32,4,1.5); +INSERT INTO Item VALUES(38,16,40,18,1.5); +INSERT INTO Item VALUES(38,15,47,10,1.5); +INSERT INTO Item VALUES(38,14,20,7,1.5); +INSERT INTO Item VALUES(38,13,8,7,1.5); +INSERT INTO Item VALUES(38,12,1,18,1.5); +INSERT INTO Item VALUES(38,11,19,18,1.5); +INSERT INTO Item VALUES(38,10,4,18,1.5); +INSERT INTO Item VALUES(38,9,27,20,1.5); +INSERT INTO Item VALUES(38,8,40,10,1.5); +INSERT INTO Item VALUES(38,7,15,1,1.5); +INSERT INTO Item VALUES(38,6,5,19,1.5); +INSERT INTO Item VALUES(38,5,48,17,1.5); +INSERT INTO Item VALUES(38,4,45,14,1.5); +INSERT INTO Item VALUES(38,3,27,19,1.5); +INSERT INTO Item VALUES(38,2,4,8,1.5); +INSERT INTO Item VALUES(38,1,45,13,1.5); +INSERT INTO Item VALUES(38,0,48,14,1.5); +INSERT INTO Item VALUES(39,3,20,17,1.5); +INSERT INTO Item VALUES(39,2,39,16,1.5); +INSERT INTO Item VALUES(39,1,24,6,1.5); +INSERT INTO Item VALUES(39,0,10,12,1.5); +INSERT INTO Item VALUES(40,20,4,16,1.5); +INSERT INTO Item VALUES(40,19,7,23,1.5); +INSERT INTO Item VALUES(40,18,33,11,1.5); +INSERT INTO Item VALUES(40,17,4,20,1.5); +INSERT INTO Item VALUES(40,16,27,16,1.5); +INSERT INTO Item VALUES(40,15,22,12,1.5); +INSERT INTO Item VALUES(40,14,4,24,1.5); +INSERT INTO Item VALUES(40,13,6,8,1.5); +INSERT INTO Item VALUES(40,12,35,13,1.5); +INSERT INTO Item VALUES(40,11,27,2,1.5); +INSERT INTO Item VALUES(40,10,6,11,1.5); +INSERT INTO Item VALUES(40,9,40,17,1.5); +INSERT INTO Item VALUES(40,8,11,4,1.5); +INSERT INTO Item VALUES(40,7,31,1,1.5); +INSERT INTO Item VALUES(40,6,28,12,1.5); +INSERT INTO Item VALUES(40,5,32,18,1.5); +INSERT INTO Item VALUES(40,4,18,13,1.5); +INSERT INTO Item VALUES(40,3,26,10,1.5); +INSERT INTO Item VALUES(40,2,4,5,1.5); +INSERT INTO Item VALUES(40,1,45,24,1.5); +INSERT INTO Item VALUES(40,0,46,24,1.5); +INSERT INTO Item VALUES(41,11,48,15,1.5); +INSERT INTO Item VALUES(41,10,24,20,1.5); +INSERT INTO Item VALUES(41,9,26,21,1.5); +INSERT INTO Item VALUES(41,8,9,22,1.5); +INSERT INTO Item VALUES(41,7,22,18,1.5); +INSERT INTO Item VALUES(41,6,17,11,1.5); +INSERT INTO Item VALUES(41,5,9,21,1.5); +INSERT INTO Item VALUES(41,4,16,22,1.5); +INSERT INTO Item VALUES(41,3,29,20,1.5); +INSERT INTO Item VALUES(41,2,36,2,1.5); +INSERT INTO Item VALUES(41,1,47,19,1.5); +INSERT INTO Item VALUES(41,0,5,24,1.5); +INSERT INTO Item VALUES(42,4,48,15,1.5); +INSERT INTO Item VALUES(42,3,40,14,1.5); +INSERT INTO Item VALUES(42,2,40,19,1.5); +INSERT INTO Item VALUES(42,1,18,21,1.5); +INSERT INTO Item VALUES(42,0,48,9,1.5); +INSERT INTO Item VALUES(43,16,38,12,1.5); +INSERT INTO Item VALUES(43,15,48,7,1.5); +INSERT INTO Item VALUES(43,14,3,18,1.5); +INSERT INTO Item VALUES(43,13,44,22,1.5); +INSERT INTO Item VALUES(43,12,40,24,1.5); +INSERT INTO Item VALUES(43,11,49,23,1.5); +INSERT INTO Item VALUES(43,10,35,1,1.5); +INSERT INTO Item VALUES(43,9,7,23,1.5); +INSERT INTO Item VALUES(43,8,44,8,1.5); +INSERT INTO Item VALUES(43,7,11,15,1.5); +INSERT INTO Item VALUES(43,6,24,1,1.5); +INSERT INTO Item VALUES(43,5,33,6,1.5); +INSERT INTO Item VALUES(43,4,32,22,1.5); +INSERT INTO Item VALUES(43,3,6,18,1.5); +INSERT INTO Item VALUES(43,2,2,15,1.5); +INSERT INTO Item VALUES(43,1,18,19,1.5); +INSERT INTO Item VALUES(43,0,15,22,1.5); +INSERT INTO Item VALUES(44,8,28,23,1.5); +INSERT INTO Item VALUES(44,7,49,17,1.5); +INSERT INTO Item VALUES(44,6,14,15,1.5); +INSERT INTO Item VALUES(44,5,41,22,1.5); +INSERT INTO Item VALUES(44,4,12,3,1.5); +INSERT INTO Item VALUES(44,3,3,14,1.5); +INSERT INTO Item VALUES(44,2,17,14,1.5); +INSERT INTO Item VALUES(44,1,34,17,1.5); +INSERT INTO Item VALUES(44,0,33,20,1.5); +INSERT INTO Item VALUES(45,14,3,16,1.5); +INSERT INTO Item VALUES(45,13,47,8,1.5); +INSERT INTO Item VALUES(45,12,32,13,1.5); +INSERT INTO Item VALUES(45,11,31,22,1.5); +INSERT INTO Item VALUES(45,10,41,24,1.5); +INSERT INTO Item VALUES(45,9,26,18,1.5); +INSERT INTO Item VALUES(45,8,9,2,1.5); +INSERT INTO Item VALUES(45,7,6,24,1.5); +INSERT INTO Item VALUES(45,6,39,5,1.5); +INSERT INTO Item VALUES(45,5,45,17,1.5); +INSERT INTO Item VALUES(45,4,3,14,1.5); +INSERT INTO Item VALUES(45,3,14,11,1.5); +INSERT INTO Item VALUES(45,2,46,8,1.5); +INSERT INTO Item VALUES(45,1,11,6,1.5); +INSERT INTO Item VALUES(45,0,44,6,1.5); +INSERT INTO Item VALUES(46,17,12,23,1.5); +INSERT INTO Item VALUES(46,16,46,21,1.5); +INSERT INTO Item VALUES(46,15,40,11,1.5); +INSERT INTO Item VALUES(46,14,24,10,1.5); +INSERT INTO Item VALUES(46,13,36,20,1.5); +INSERT INTO Item VALUES(46,12,21,24,1.5); +INSERT INTO Item VALUES(46,11,1,4,1.5); +INSERT INTO Item VALUES(46,10,11,24,1.5); +INSERT INTO Item VALUES(46,9,7,4,1.5); +INSERT INTO Item VALUES(46,8,8,22,1.5); +INSERT INTO Item VALUES(46,7,49,9,1.5); +INSERT INTO Item VALUES(46,6,41,18,1.5); +INSERT INTO Item VALUES(46,5,25,9,1.5); +INSERT INTO Item VALUES(46,4,17,5,1.5); +INSERT INTO Item VALUES(46,3,21,19,1.5); +INSERT INTO Item VALUES(46,2,30,14,1.5); +INSERT INTO Item VALUES(46,1,12,24,1.5); +INSERT INTO Item VALUES(46,0,5,21,1.5); +INSERT INTO Item VALUES(47,13,33,8,1.5); +INSERT INTO Item VALUES(47,12,12,20,1.5); +INSERT INTO Item VALUES(47,11,35,10,1.5); +INSERT INTO Item VALUES(47,10,45,2,1.5); +INSERT INTO Item VALUES(47,9,32,9,1.5); +INSERT INTO Item VALUES(47,8,16,2,1.5); +INSERT INTO Item VALUES(47,7,28,14,1.5); +INSERT INTO Item VALUES(47,6,8,10,1.5); +INSERT INTO Item VALUES(47,5,40,8,1.5); +INSERT INTO Item VALUES(47,4,15,1,1.5); +INSERT INTO Item VALUES(47,3,1,4,1.5); +INSERT INTO Item VALUES(47,2,17,6,1.5); +INSERT INTO Item VALUES(47,1,23,13,1.5); +INSERT INTO Item VALUES(47,0,23,15,1.5); +INSERT INTO Item VALUES(48,10,41,10,1.5); +INSERT INTO Item VALUES(48,9,35,17,1.5); +INSERT INTO Item VALUES(48,8,5,12,1.5); +INSERT INTO Item VALUES(48,7,30,19,1.5); +INSERT INTO Item VALUES(48,6,11,17,1.5); +INSERT INTO Item VALUES(48,5,24,16,1.5); +INSERT INTO Item VALUES(48,4,48,4,1.5); +INSERT INTO Item VALUES(48,3,10,2,1.5); +INSERT INTO Item VALUES(48,2,23,10,1.5); +INSERT INTO Item VALUES(48,1,26,23,1.5); +INSERT INTO Item VALUES(48,0,6,23,1.5); +INSERT INTO Item VALUES(49,16,24,18,1.5); +INSERT INTO Item VALUES(49,15,19,24,1.5); +INSERT INTO Item VALUES(49,14,23,5,1.5); +INSERT INTO Item VALUES(49,13,6,22,1.5); +INSERT INTO Item VALUES(49,12,21,17,1.5); +INSERT INTO Item VALUES(49,11,40,15,1.5); +INSERT INTO Item VALUES(49,10,30,16,1.5); +INSERT INTO Item VALUES(49,9,7,24,1.5); +INSERT INTO Item VALUES(49,8,48,24,1.5); +INSERT INTO Item VALUES(49,7,6,21,1.5); +INSERT INTO Item VALUES(49,6,29,15,1.5); +INSERT INTO Item VALUES(49,5,16,1,1.5); +INSERT INTO Item VALUES(49,4,47,14,1.5); +INSERT INTO Item VALUES(49,3,17,19,1.5); +INSERT INTO Item VALUES(49,2,29,6,1.5); +INSERT INTO Item VALUES(49,1,22,16,1.5); +INSERT INTO Item VALUES(49,0,18,6,1.5); +UPDATE Product SET Price=ROUND(Price*.1,2); +UPDATE Item SET Cost=Cost*(SELECT Price FROM Product prod WHERE ProductID=prod.ID); +UPDATE Invoice SET Total=SELECT SUM(Cost*Quantity) FROM Item WHERE InvoiceID=Invoice.ID; + +COMMIT; diff --git a/examples/spring-batch/src/test/resources/job.properties b/examples/spring-batch/src/test/resources/job.properties new file mode 100644 index 0000000000..474f46e53c --- /dev/null +++ b/examples/spring-batch/src/test/resources/job.properties @@ -0,0 +1,13 @@ +marklogic.host=oscar +marklogic.port=8000 +marklogic.username=admin +marklogic.password=admin +marklogic.database=Documents + +marklogic.jobrepo.host=oscar +marklogic.jobrepo.port=8201 +marklogic.jobrepo.username=admin +marklogic.jobrepo.password=admin +marklogic.jobrepo.database=mlJobRepo-content + +marklogic.batch.config.enabled=true From 3069c91ec03efbe1d92e824852c20f8f10021ab3 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Thu, 3 Aug 2017 22:46:32 -0400 Subject: [PATCH 02/15] Change root name to table name and place into collection --- .../hub/job/AllTablesColumnMapProcessor.java | 82 +++++++++++++++++++ .../hub/job/SqlDbToHubJobConfig.java | 6 +- 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 examples/spring-batch/src/main/java/com/marklogic/hub/job/AllTablesColumnMapProcessor.java diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/AllTablesColumnMapProcessor.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/AllTablesColumnMapProcessor.java new file mode 100644 index 0000000000..44ddf7e186 --- /dev/null +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/AllTablesColumnMapProcessor.java @@ -0,0 +1,82 @@ +package com.marklogic.hub.job; + +import com.marklogic.client.document.DocumentWriteOperation; +import com.marklogic.client.impl.DocumentWriteOperationImpl; +import com.marklogic.client.io.DocumentMetadataHandle; +import com.marklogic.client.io.StringHandle; +import com.marklogic.spring.batch.columnmap.ColumnMapSerializer; +import org.springframework.batch.item.ItemProcessor; + +import java.util.Map; +import java.util.UUID; + +/** + * This is a very basic processor for taking a column map (a Map) and serializing it via a + * ColumnMapSerializer, and then providing very basic support for setting permissions and collections. + * marklogic-spring-batch provides other options for e.g. customizing the URI. Feel free to customize any way you'd like. + */ +public class AllTablesColumnMapProcessor implements ItemProcessor, DocumentWriteOperation> { + + private ColumnMapSerializer columnMapSerializer; + private String tableNameKey = "_tableName"; + private String rootLocalName = "CHANGEME"; + + // Expected to be role,capability,role,capability,etc. + private String[] permissions; + + private String[] collections; + + public AllTablesColumnMapProcessor(ColumnMapSerializer columnMapSerializer) { + this.columnMapSerializer = columnMapSerializer; + } + + @Override + public DocumentWriteOperation process(Map item) throws Exception { + String tableName = null; + if (item.containsKey(tableNameKey)) { + tableName = (String)item.get(tableNameKey); + item.remove(tableNameKey); + } + + String thisRootLocalName = tableName != null ? tableName : rootLocalName; + String content = columnMapSerializer.serializeColumnMap(item, thisRootLocalName); + + String uuid = UUID.randomUUID().toString(); + String uri = "/" + thisRootLocalName + "/" + uuid + ".xml"; + + DocumentMetadataHandle metadata = new DocumentMetadataHandle(); + if (collections != null) { + metadata.withCollections(collections); + } + if (tableName != null) { + metadata.withCollections(tableName); + } + + if (permissions != null) { + for (int i = 0; i < permissions.length; i += 2) { + String role = permissions[i]; + DocumentMetadataHandle.Capability c = DocumentMetadataHandle.Capability.valueOf(permissions[i + 1].toUpperCase()); + metadata.withPermission(role, c); + } + } + + return new DocumentWriteOperationImpl(DocumentWriteOperation.OperationType.DOCUMENT_WRITE, + uri, metadata, new StringHandle(content)); + } + + public void setRootLocalName(String rootLocalName) { + this.rootLocalName = rootLocalName; + } + + public void setCollections(String[] collections) { + this.collections = collections; + } + + public void setPermissions(String[] permissions) { + this.permissions = permissions; + } + + public void setTableNameKey(String tableNameKey) { + this.tableNameKey = tableNameKey; + } +} diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java index aa27efcac6..99efa8f4f6 100644 --- a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java @@ -4,6 +4,7 @@ import com.marklogic.client.helper.DatabaseClientProvider; import com.marklogic.spring.batch.columnmap.ColumnMapSerializer; import com.marklogic.spring.batch.columnmap.DefaultStaxColumnMapSerializer; +import com.marklogic.spring.batch.columnmap.XmlStringColumnMapSerializer; import com.marklogic.spring.batch.item.processor.ColumnMapProcessor; import com.marklogic.spring.batch.item.reader.AllTablesItemReader; import com.marklogic.spring.batch.item.writer.MarkLogicItemWriter; @@ -13,6 +14,7 @@ import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.JobScope; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemWriter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -43,8 +45,8 @@ public Step step( AllTablesItemReader reader = new AllTablesItemReader(dataSource); // Processor - this is a very basic implementation for converting a column map to an XML string - ColumnMapSerializer serializer = new DefaultStaxColumnMapSerializer(); - ColumnMapProcessor processor = new ColumnMapProcessor(serializer); + ColumnMapSerializer serializer = new XmlStringColumnMapSerializer(); + ItemProcessor processor = new AllTablesColumnMapProcessor(serializer); ItemWriter writer = new MarkLogicItemWriter(databaseClientProvider.getDatabaseClient()); From 875b1e5f9edf2df0ba029b3bf7aa2d0018e92607 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Thu, 3 Aug 2017 22:50:43 -0400 Subject: [PATCH 03/15] Add DataHubItemWriter to batch job --- .../marklogic/hub/job/SqlDbToHubJobConfig.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java index 99efa8f4f6..6ebdee4a4d 100644 --- a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java @@ -1,5 +1,6 @@ package com.marklogic.hub.job; +import com.marklogic.client.DatabaseClient; import com.marklogic.client.document.DocumentWriteOperation; import com.marklogic.client.helper.DatabaseClientProvider; import com.marklogic.spring.batch.columnmap.ColumnMapSerializer; @@ -7,6 +8,7 @@ import com.marklogic.spring.batch.columnmap.XmlStringColumnMapSerializer; import com.marklogic.spring.batch.item.processor.ColumnMapProcessor; import com.marklogic.spring.batch.item.reader.AllTablesItemReader; +import com.marklogic.spring.batch.item.writer.DataHubItemWriter; import com.marklogic.spring.batch.item.writer.MarkLogicItemWriter; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; @@ -40,7 +42,9 @@ public Step step( StepBuilderFactory stepBuilderFactory, DataSource dataSource, DatabaseClientProvider databaseClientProvider, - @Value("#{jobParameters['output_collections']}") String[] collections) { + @Value("#{jobParameters['entity']}") String entity, + @Value("#{jobParameters['flow']}") String flow, + @Value("#{jobParameters['job_id']}") String jobId) { AllTablesItemReader reader = new AllTablesItemReader(dataSource); @@ -48,7 +52,14 @@ public Step step( ColumnMapSerializer serializer = new XmlStringColumnMapSerializer(); ItemProcessor processor = new AllTablesColumnMapProcessor(serializer); - ItemWriter writer = new MarkLogicItemWriter(databaseClientProvider.getDatabaseClient()); + // Uncomment to push data directly into MarkLogic without going through DataHub + //ItemWriter writer = new MarkLogicItemWriter(databaseClientProvider.getDatabaseClient()); + + ItemWriter writer = new DataHubItemWriter(databaseClientProvider.getDatabaseClient(), + entity, + DataHubItemWriter.FlowType.INPUT, + flow, + jobId); return stepBuilderFactory.get("step1") ., DocumentWriteOperation>chunk(10) From b6c37d4492b9bd55f9a12e46406c560f66240605 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Thu, 3 Aug 2017 23:06:07 -0400 Subject: [PATCH 04/15] Add properties to accept data hub parameters --- .../java/com/marklogic/hub/job/SqlDbToHubJobTest.java | 3 +++ examples/spring-batch/src/test/resources/job.properties | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java index 1c543eed23..0cda3c52cf 100644 --- a/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java +++ b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java @@ -29,6 +29,9 @@ public void migrateSampleDatabaseToDataHubTest() { JobParametersBuilder jpb = new JobParametersBuilder(); jpb.addString("allTables", "true"); jpb.addString("date", new Date().toString(), true); + jpb.addString("entity", "Monster"); + jpb.addString("flow", "ingest-monster"); + jpb.addString("job_id", "1234"); try { JobExecution execution = jobLauncherTestUtils.launchJob(jpb.toJobParameters()); assertEquals(BatchStatus.COMPLETED, execution.getStatus()); diff --git a/examples/spring-batch/src/test/resources/job.properties b/examples/spring-batch/src/test/resources/job.properties index 474f46e53c..eb629ef5a4 100644 --- a/examples/spring-batch/src/test/resources/job.properties +++ b/examples/spring-batch/src/test/resources/job.properties @@ -1,13 +1,13 @@ marklogic.host=oscar -marklogic.port=8000 +marklogic.port=8010 marklogic.username=admin marklogic.password=admin -marklogic.database=Documents +marklogic.database=data-hub-STAGING marklogic.jobrepo.host=oscar -marklogic.jobrepo.port=8201 +marklogic.jobrepo.port=8015 marklogic.jobrepo.username=admin marklogic.jobrepo.password=admin -marklogic.jobrepo.database=mlJobRepo-content +marklogic.jobrepo.database=ml-job-repo marklogic.batch.config.enabled=true From 176c1c1d965a9704f8e44ddb0721c3ef34b86e40 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Thu, 3 Aug 2017 23:07:20 -0400 Subject: [PATCH 05/15] Upgrade to gradle wrapper 4.0 --- .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54706 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + examples/spring-batch/gradlew | 172 ++++++++++++++++++ examples/spring-batch/gradlew.bat | 84 +++++++++ 4 files changed, 262 insertions(+) create mode 100644 examples/spring-batch/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/spring-batch/gradle/wrapper/gradle-wrapper.properties create mode 100644 examples/spring-batch/gradlew create mode 100644 examples/spring-batch/gradlew.bat diff --git a/examples/spring-batch/gradle/wrapper/gradle-wrapper.jar b/examples/spring-batch/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..66c54171086475938ccc32b0cfcf5bdc64467131 GIT binary patch literal 54706 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 literal 0 HcmV?d00001 diff --git a/examples/spring-batch/gradle/wrapper/gradle-wrapper.properties b/examples/spring-batch/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..aae0328361 --- /dev/null +++ b/examples/spring-batch/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Aug 03 23:06:41 EDT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip diff --git a/examples/spring-batch/gradlew b/examples/spring-batch/gradlew new file mode 100644 index 0000000000..cccdd3d517 --- /dev/null +++ b/examples/spring-batch/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/examples/spring-batch/gradlew.bat b/examples/spring-batch/gradlew.bat new file mode 100644 index 0000000000..f9553162f1 --- /dev/null +++ b/examples/spring-batch/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 59d25b501c0139aaf00f0e598cec38797648da92 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Fri, 4 Aug 2017 11:42:41 -0400 Subject: [PATCH 06/15] Add rdbms entity/relationship diagram --- examples/spring-batch/invoices-sql-diagram.jpg | Bin 0 -> 21404 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/spring-batch/invoices-sql-diagram.jpg diff --git a/examples/spring-batch/invoices-sql-diagram.jpg b/examples/spring-batch/invoices-sql-diagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7e5607c364fed4693f120774e15d572860e1d47d GIT binary patch literal 21404 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 literal 0 HcmV?d00001 From 37d854d63965cf39b9e0078bd389de73b96b9491 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Fri, 4 Aug 2017 13:03:29 -0400 Subject: [PATCH 07/15] Add new tasks to load and view data in H2 --- .gitignore | 1 + examples/spring-batch/build.gradle | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 91fdca1fa3..4a379bde87 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ node_modules/ *.iml *.ipr *.iws +*.db diff --git a/examples/spring-batch/build.gradle b/examples/spring-batch/build.gradle index 03304ab466..98593cb921 100644 --- a/examples/spring-batch/build.gradle +++ b/examples/spring-batch/build.gradle @@ -18,15 +18,17 @@ dependencies { testCompile "com.h2database:h2:1.4.193" testCompile "com.marklogic:marklogic-spring-batch-test:1.0.1" + + runtime "com.h2database:h2:1.4.193" } -mainClassName = "com.marklogic.spring.batch.Main" +mainClassName = "com.marklogic.spring.batch.core.launch.support.CommandLineJobRunner" task importMonsters(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath - main = "com.marklogic.spring.batch.Main" + main = "com.marklogic.spring.batch.core.launch.support.CommandLineJobRunner" args = [ - "--config", "example.LoadAndRunFlow", + "--jobPath", "com", "--project_dir", ".", "--env", "local", "--input_file_path", "./input", @@ -36,3 +38,23 @@ task importMonsters(type: JavaExec) { "--chunk", "100" ] } + +// This task is for running the examples +task runH2DataManager(type: JavaExec) { + classpath = configurations.runtime + main = "org.h2.tools.Console" + args = [ + "-url", "jdbc:h2:file:./input/sample", + "-user", "sa" + ] +} + +task loadH2Data(type: JavaExec) { + classpath = configurations.runtime + main = "org.h2.tools.RunScript" + args = [ + "-url", "jdbc:h2:file:./input/sample", + "-user", "sa", + "-script", "./src/test/resources/db/sampledata.sql" + ] +} From 41718f4a460f3afdb0e4dcba551892daca9a441d Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 11:16:08 -0400 Subject: [PATCH 08/15] Update to 2.0 version of Data Hub, add Invoice entity, remove monster entity --- examples/spring-batch/build.gradle | 19 ++++++------ .../databases/final-database.json | 1 + .../databases/staging-database.json | 1 + .../entity-config/entity-options.xml | 28 ++++++++++++++++++ examples/spring-batch/job.properties | 13 +++++++++ .../entities/Invoice/Invoice.entity.json | 19 ++++++++++++ .../collector/collector.xqy | 20 +++++++++++++ .../harmonize-invoices/content/content.xqy | 29 +++++++++++++++++++ .../harmonize-invoices.xml} | 8 +++-- .../harmonize-invoices/headers/headers.xqy | 24 +++++++++++++++ .../harmonize-invoices/triples/triples.xqy | 26 +++++++++++++++++ .../harmonize-invoices/writer/writer.xqy | 22 ++++++++++++++ .../ingest-invoice-db}/content/content.xqy | 0 .../ingest-invoice-db}/headers/headers.xqy | 0 .../ingest-invoice-db/ingest-invoice-db.xml | 10 +++++++ .../ingest-invoice-db}/triples/triples.xqy | 0 .../hub/job/SqlDbToHubJobConfig.java | 14 ++++----- .../hub/job}/h2/H2DatabaseConfiguration.java | 2 +- .../marklogic/hub/job/SqlDbToHubJobTest.java | 4 +-- 19 files changed, 217 insertions(+), 23 deletions(-) create mode 100644 examples/spring-batch/entity-config/databases/final-database.json create mode 100644 examples/spring-batch/entity-config/databases/staging-database.json create mode 100644 examples/spring-batch/entity-config/entity-options.xml create mode 100644 examples/spring-batch/job.properties create mode 100644 examples/spring-batch/plugins/entities/Invoice/Invoice.entity.json create mode 100644 examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/collector/collector.xqy create mode 100644 examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/content/content.xqy rename examples/spring-batch/plugins/entities/{Monster/input/ingest-monster/ingest-monster.xml => Invoice/harmonize/harmonize-invoices/harmonize-invoices.xml} (60%) create mode 100644 examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/headers/headers.xqy create mode 100644 examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/triples/triples.xqy create mode 100644 examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/writer/writer.xqy rename examples/spring-batch/plugins/entities/{Monster/input/ingest-monster => Invoice/input/ingest-invoice-db}/content/content.xqy (100%) rename examples/spring-batch/plugins/entities/{Monster/input/ingest-monster => Invoice/input/ingest-invoice-db}/headers/headers.xqy (100%) create mode 100644 examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/ingest-invoice-db.xml rename examples/spring-batch/plugins/entities/{Monster/input/ingest-monster => Invoice/input/ingest-invoice-db}/triples/triples.xqy (100%) rename examples/spring-batch/src/{test/java/com/marklogic/sample => main/java/com/marklogic/hub/job}/h2/H2DatabaseConfiguration.java (96%) diff --git a/examples/spring-batch/build.gradle b/examples/spring-batch/build.gradle index 98593cb921..279e2c5675 100644 --- a/examples/spring-batch/build.gradle +++ b/examples/spring-batch/build.gradle @@ -20,22 +20,21 @@ dependencies { testCompile "com.marklogic:marklogic-spring-batch-test:1.0.1" runtime "com.h2database:h2:1.4.193" + runtime "ch.qos.logback:logback-classic:1.1.8" + runtime "org.slf4j:jcl-over-slf4j:1.7.22" + runtime "org.slf4j:slf4j-api:1.7.22" } mainClassName = "com.marklogic.spring.batch.core.launch.support.CommandLineJobRunner" -task importMonsters(type: JavaExec) { +task ingestInvoices(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath main = "com.marklogic.spring.batch.core.launch.support.CommandLineJobRunner" args = [ - "--jobPath", "com", - "--project_dir", ".", - "--env", "local", - "--input_file_path", "./input", - "--input_file_pattern", ".*\\.xml", - "--entity_name", "Monster", - "--flow_name", "ingest-monster", - "--chunk", "100" + "--job_path", "com.marklogic.hub.job.SqlDbToHubJobConfig", + "--job_id", "job", + "--entity", "Invoice", + "--flow", "ingest-invoice-db" ] } @@ -58,3 +57,5 @@ task loadH2Data(type: JavaExec) { "-script", "./src/test/resources/db/sampledata.sql" ] } + +ingestInvoices.dependsOn loadH2Data diff --git a/examples/spring-batch/entity-config/databases/final-database.json b/examples/spring-batch/entity-config/databases/final-database.json new file mode 100644 index 0000000000..459dd2e1e3 --- /dev/null +++ b/examples/spring-batch/entity-config/databases/final-database.json @@ -0,0 +1 @@ +{"path-namespace":[{"prefix":"es", "namespace-uri":"http://marklogic.com/entity-services"}], "element-word-lexicon":[], "range-path-index":[], "range-element-index":[]} \ No newline at end of file diff --git a/examples/spring-batch/entity-config/databases/staging-database.json b/examples/spring-batch/entity-config/databases/staging-database.json new file mode 100644 index 0000000000..459dd2e1e3 --- /dev/null +++ b/examples/spring-batch/entity-config/databases/staging-database.json @@ -0,0 +1 @@ +{"path-namespace":[{"prefix":"es", "namespace-uri":"http://marklogic.com/entity-services"}], "element-word-lexicon":[], "range-path-index":[], "range-element-index":[]} \ No newline at end of file diff --git a/examples/spring-batch/entity-config/entity-options.xml b/examples/spring-batch/entity-config/entity-options.xml new file mode 100644 index 0000000000..781863125d --- /dev/null +++ b/examples/spring-batch/entity-config/entity-options.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + unfiltered + + + /*:envelope/*:instance/(Invoice) + + true + + + \ No newline at end of file diff --git a/examples/spring-batch/job.properties b/examples/spring-batch/job.properties new file mode 100644 index 0000000000..eb629ef5a4 --- /dev/null +++ b/examples/spring-batch/job.properties @@ -0,0 +1,13 @@ +marklogic.host=oscar +marklogic.port=8010 +marklogic.username=admin +marklogic.password=admin +marklogic.database=data-hub-STAGING + +marklogic.jobrepo.host=oscar +marklogic.jobrepo.port=8015 +marklogic.jobrepo.username=admin +marklogic.jobrepo.password=admin +marklogic.jobrepo.database=ml-job-repo + +marklogic.batch.config.enabled=true diff --git a/examples/spring-batch/plugins/entities/Invoice/Invoice.entity.json b/examples/spring-batch/plugins/entities/Invoice/Invoice.entity.json new file mode 100644 index 0000000000..1a5798a8de --- /dev/null +++ b/examples/spring-batch/plugins/entities/Invoice/Invoice.entity.json @@ -0,0 +1,19 @@ +{ + "info" : { + "title" : "Invoice", + "version" : "0.0.1" + }, + "definitions" : { + "Invoice" : { + "required" : [ ], + "rangeIndex" : [ ], + "wordLexicon" : [ ], + "properties" : { + "id" : { + "datatype" : "string", + "collation" : "http://marklogic.com/collation/codepoint" + } + } + } + } +} \ No newline at end of file diff --git a/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/collector/collector.xqy b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/collector/collector.xqy new file mode 100644 index 0000000000..999fd43c74 --- /dev/null +++ b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/collector/collector.xqy @@ -0,0 +1,20 @@ +xquery version "1.0-ml"; + +module namespace plugin = "http://marklogic.com/data-hub/plugins"; + +declare option xdmp:mapping "false"; + +(:~ + : Collect IDs plugin + : + : @param $options - a map containing options. Options are sent from Java + : + : @return - a sequence of ids or uris + :) +declare function plugin:collect( + $options as map:map) as xs:string* +{ + (: by default we return the URIs in the same collection as the Entity name :) + cts:uris((), (), cts:collection-query(map:get($options, "entity"))) +}; + diff --git a/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/content/content.xqy b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/content/content.xqy new file mode 100644 index 0000000000..3264abf077 --- /dev/null +++ b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/content/content.xqy @@ -0,0 +1,29 @@ +xquery version "1.0-ml"; + +module namespace plugin = "http://marklogic.com/data-hub/plugins"; + +declare namespace envelope = "http://marklogic.com/data-hub/envelope"; + +declare option xdmp:mapping "false"; + +(:~ + : Create Content Plugin + : + : @param $id - the identifier returned by the collector + : @param $options - a map containing options. Options are sent from Java + : + : @return - your transformed content + :) +declare function plugin:create-content( + $id as xs:string, + $options as map:map) as node()? +{ + let $doc := fn:doc($id) + return + if ($doc/envelope:envelope) then + $doc/envelope:envelope/envelope:content/node() + else if ($doc/content) then + $doc/content + else + $doc +}; diff --git a/examples/spring-batch/plugins/entities/Monster/input/ingest-monster/ingest-monster.xml b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/harmonize-invoices.xml similarity index 60% rename from examples/spring-batch/plugins/entities/Monster/input/ingest-monster/ingest-monster.xml rename to examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/harmonize-invoices.xml index 31ca01c002..b7b148d6d9 100644 --- a/examples/spring-batch/plugins/entities/Monster/input/ingest-monster/ingest-monster.xml +++ b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/harmonize-invoices.xml @@ -1,8 +1,10 @@ + + - simple - application/xml - + simple + application/xml + diff --git a/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/headers/headers.xqy b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/headers/headers.xqy new file mode 100644 index 0000000000..f491716ff3 --- /dev/null +++ b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/headers/headers.xqy @@ -0,0 +1,24 @@ +xquery version "1.0-ml"; + +module namespace plugin = "http://marklogic.com/data-hub/plugins"; + +declare namespace envelope = "http://marklogic.com/data-hub/envelope"; + +declare option xdmp:mapping "false"; + +(:~ + : Create Headers Plugin + : + : @param $id - the identifier returned by the collector + : @param $content - the output of your content plugin + : @param $options - a map containing options. Options are sent from Java + : + : @return - zero or more header nodes + :) +declare function plugin:create-headers( + $id as xs:string, + $content as item()?, + $options as map:map) as node()* +{ + () +}; diff --git a/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/triples/triples.xqy b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/triples/triples.xqy new file mode 100644 index 0000000000..9ae12331a0 --- /dev/null +++ b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/triples/triples.xqy @@ -0,0 +1,26 @@ +xquery version "1.0-ml"; + +module namespace plugin = "http://marklogic.com/data-hub/plugins"; + +declare namespace envelope = "http://marklogic.com/data-hub/envelope"; + +declare option xdmp:mapping "false"; + +(:~ + : Create Triples Plugin + : + : @param $id - the identifier returned by the collector + : @param $content - the output of your content plugin + : @param $headers - the output of your headers plugin + : @param $options - a map containing options. Options are sent from Java + : + : @return - zero or more triples + :) +declare function plugin:create-triples( + $id as xs:string, + $content as item()?, + $headers as item()*, + $options as map:map) as sem:triple* +{ + () +}; diff --git a/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/writer/writer.xqy b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/writer/writer.xqy new file mode 100644 index 0000000000..78d16c3fd9 --- /dev/null +++ b/examples/spring-batch/plugins/entities/Invoice/harmonize/harmonize-invoices/writer/writer.xqy @@ -0,0 +1,22 @@ +xquery version "1.0-ml"; + +module namespace plugin = "http://marklogic.com/data-hub/plugins"; + +declare option xdmp:mapping "false"; + +(:~ + : Writer Plugin + : + : @param $id - the identifier returned by the collector + : @param $envelope - the final envelope + : @param $options - a map containing options. Options are sent from Java + : + : @return - nothing + :) +declare function plugin:write( + $id as xs:string, + $envelope as node(), + $options as map:map) as empty-sequence() +{ + xdmp:document-insert($id, $envelope, xdmp:default-permissions(), map:get($options, "entity")) +}; diff --git a/examples/spring-batch/plugins/entities/Monster/input/ingest-monster/content/content.xqy b/examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/content/content.xqy similarity index 100% rename from examples/spring-batch/plugins/entities/Monster/input/ingest-monster/content/content.xqy rename to examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/content/content.xqy diff --git a/examples/spring-batch/plugins/entities/Monster/input/ingest-monster/headers/headers.xqy b/examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/headers/headers.xqy similarity index 100% rename from examples/spring-batch/plugins/entities/Monster/input/ingest-monster/headers/headers.xqy rename to examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/headers/headers.xqy diff --git a/examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/ingest-invoice-db.xml b/examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/ingest-invoice-db.xml new file mode 100644 index 0000000000..b7b148d6d9 --- /dev/null +++ b/examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/ingest-invoice-db.xml @@ -0,0 +1,10 @@ + + + + + + simple + application/xml + + + diff --git a/examples/spring-batch/plugins/entities/Monster/input/ingest-monster/triples/triples.xqy b/examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/triples/triples.xqy similarity index 100% rename from examples/spring-batch/plugins/entities/Monster/input/ingest-monster/triples/triples.xqy rename to examples/spring-batch/plugins/entities/Invoice/input/ingest-invoice-db/triples/triples.xqy diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java index 6ebdee4a4d..efea0a72ea 100644 --- a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java @@ -1,15 +1,11 @@ package com.marklogic.hub.job; -import com.marklogic.client.DatabaseClient; import com.marklogic.client.document.DocumentWriteOperation; import com.marklogic.client.helper.DatabaseClientProvider; import com.marklogic.spring.batch.columnmap.ColumnMapSerializer; -import com.marklogic.spring.batch.columnmap.DefaultStaxColumnMapSerializer; import com.marklogic.spring.batch.columnmap.XmlStringColumnMapSerializer; -import com.marklogic.spring.batch.item.processor.ColumnMapProcessor; import com.marklogic.spring.batch.item.reader.AllTablesItemReader; import com.marklogic.spring.batch.item.writer.DataHubItemWriter; -import com.marklogic.spring.batch.item.writer.MarkLogicItemWriter; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; @@ -24,9 +20,12 @@ import javax.sql.DataSource; import java.util.Map; +import java.util.UUID; @EnableBatchProcessing -@Import(value = {com.marklogic.spring.batch.config.MarkLogicBatchConfiguration.class }) +@Import(value = { + com.marklogic.spring.batch.config.MarkLogicBatchConfiguration.class, + com.marklogic.hub.job.h2.H2DatabaseConfiguration.class}) public class SqlDbToHubJobConfig { private final String JOB_NAME = "sqlDbToHubJob"; @@ -43,8 +42,7 @@ public Step step( DataSource dataSource, DatabaseClientProvider databaseClientProvider, @Value("#{jobParameters['entity']}") String entity, - @Value("#{jobParameters['flow']}") String flow, - @Value("#{jobParameters['job_id']}") String jobId) { + @Value("#{jobParameters['flow']}") String flow) { AllTablesItemReader reader = new AllTablesItemReader(dataSource); @@ -59,7 +57,7 @@ public Step step( entity, DataHubItemWriter.FlowType.INPUT, flow, - jobId); + UUID.randomUUID().toString()); return stepBuilderFactory.get("step1") ., DocumentWriteOperation>chunk(10) diff --git a/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/h2/H2DatabaseConfiguration.java similarity index 96% rename from examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java rename to examples/spring-batch/src/main/java/com/marklogic/hub/job/h2/H2DatabaseConfiguration.java index 8b703a5a2a..b1bfc1ee59 100644 --- a/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/h2/H2DatabaseConfiguration.java @@ -1,4 +1,4 @@ -package com.marklogic.sample.h2; +package com.marklogic.hub.job.h2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java index 0cda3c52cf..2302f1282e 100644 --- a/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java +++ b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java @@ -1,6 +1,6 @@ package com.marklogic.hub.job; -import com.marklogic.sample.h2.H2DatabaseConfiguration; +import com.marklogic.hub.job.h2.H2DatabaseConfiguration; import com.marklogic.spring.batch.test.AbstractJobRunnerTest; import org.junit.Test; import org.junit.runner.RunWith; @@ -15,7 +15,7 @@ import java.util.Date; @ContextConfiguration( - classes = {SqlDbToHubJobConfig.class, H2DatabaseConfiguration.class} + classes = {SqlDbToHubJobConfig.class} // initializers = {H2MockingApplicationContextInitializer.class} ) @RunWith(SpringJUnit4ClassRunner.class) From 48cd222bb8dfa60860aecd796a4fce38c5f507e7 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 11:50:17 -0400 Subject: [PATCH 09/15] Run invoice ingest from gradle command --- examples/spring-batch/build.gradle | 2 +- examples/spring-batch/job.properties | 5 ++ .../hub/job/MigrateInvoicesConfiguration.java | 32 ++++++++++++ .../hub/job/SqlDbToHubJobConfig.java | 4 +- .../marklogic/hub/job/SqlDbToHubJobTest.java | 7 +-- .../sample}/h2/H2DatabaseConfiguration.java | 4 +- ...2MockingApplicationContextInitializer.java | 49 ------------------- 7 files changed, 44 insertions(+), 59 deletions(-) create mode 100644 examples/spring-batch/src/main/java/com/marklogic/hub/job/MigrateInvoicesConfiguration.java rename examples/spring-batch/src/{main/java/com/marklogic/hub/job => test/java/com/marklogic/sample}/h2/H2DatabaseConfiguration.java (86%) delete mode 100644 examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java diff --git a/examples/spring-batch/build.gradle b/examples/spring-batch/build.gradle index 279e2c5675..cbb337fb17 100644 --- a/examples/spring-batch/build.gradle +++ b/examples/spring-batch/build.gradle @@ -31,7 +31,7 @@ task ingestInvoices(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath main = "com.marklogic.spring.batch.core.launch.support.CommandLineJobRunner" args = [ - "--job_path", "com.marklogic.hub.job.SqlDbToHubJobConfig", + "--job_path", "com.marklogic.hub.job.MigrateInvoicesConfiguration", "--job_id", "job", "--entity", "Invoice", "--flow", "ingest-invoice-db" diff --git a/examples/spring-batch/job.properties b/examples/spring-batch/job.properties index eb629ef5a4..4d4e62ae52 100644 --- a/examples/spring-batch/job.properties +++ b/examples/spring-batch/job.properties @@ -11,3 +11,8 @@ marklogic.jobrepo.password=admin marklogic.jobrepo.database=ml-job-repo marklogic.batch.config.enabled=true + +jdbc.driver=org.h2.Driver +jdbc.url=jdbc:h2:file:./input/sample +jdbc.username=sa +jdbc.password= diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/MigrateInvoicesConfiguration.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/MigrateInvoicesConfiguration.java new file mode 100644 index 0000000000..27d74e16bb --- /dev/null +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/MigrateInvoicesConfiguration.java @@ -0,0 +1,32 @@ +package com.marklogic.hub.job; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.sql.DataSource; + +@Configuration() +@Import(SqlDbToHubJobConfig.class) +@PropertySource(value = "file:job.properties") +public class MigrateInvoicesConfiguration { + + @Bean + public DataSource dataSource( + @Value("${jdbc.driver:null}") String jdbcDriverClassName, + @Value("${jdbc.url:null}") String jdbcUrl, + @Value("${jdbc.username:sa}") String username, + @Value("${jdbc.password:sa}") String password + + ) { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(jdbcDriverClassName); + dataSource.setUrl(jdbcUrl); + dataSource.setUsername(username); + dataSource.setPassword(password); + return dataSource; + } +} diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java index efea0a72ea..b40c9dfc6f 100644 --- a/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java +++ b/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java @@ -23,9 +23,7 @@ import java.util.UUID; @EnableBatchProcessing -@Import(value = { - com.marklogic.spring.batch.config.MarkLogicBatchConfiguration.class, - com.marklogic.hub.job.h2.H2DatabaseConfiguration.class}) +@Import(value = {com.marklogic.spring.batch.config.MarkLogicBatchConfiguration.class}) public class SqlDbToHubJobConfig { private final String JOB_NAME = "sqlDbToHubJob"; diff --git a/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java index 2302f1282e..d70109c19c 100644 --- a/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java +++ b/examples/spring-batch/src/test/java/com/marklogic/hub/job/SqlDbToHubJobTest.java @@ -1,6 +1,6 @@ package com.marklogic.hub.job; -import com.marklogic.hub.job.h2.H2DatabaseConfiguration; +import com.marklogic.sample.h2.H2DatabaseConfiguration; import com.marklogic.spring.batch.test.AbstractJobRunnerTest; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,10 +14,7 @@ import java.util.Date; -@ContextConfiguration( - classes = {SqlDbToHubJobConfig.class} - // initializers = {H2MockingApplicationContextInitializer.class} -) +@ContextConfiguration(classes = {SqlDbToHubJobConfig.class, H2DatabaseConfiguration.class}) @RunWith(SpringJUnit4ClassRunner.class) public class SqlDbToHubJobTest extends AbstractJobRunnerTest { diff --git a/examples/spring-batch/src/main/java/com/marklogic/hub/job/h2/H2DatabaseConfiguration.java b/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java similarity index 86% rename from examples/spring-batch/src/main/java/com/marklogic/hub/job/h2/H2DatabaseConfiguration.java rename to examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java index b1bfc1ee59..ce15051e5c 100644 --- a/examples/spring-batch/src/main/java/com/marklogic/hub/job/h2/H2DatabaseConfiguration.java +++ b/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2DatabaseConfiguration.java @@ -1,7 +1,9 @@ -package com.marklogic.hub.job.h2; +package com.marklogic.sample.h2; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; diff --git a/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java b/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java deleted file mode 100644 index 42bfedc6d7..0000000000 --- a/examples/spring-batch/src/test/java/com/marklogic/sample/h2/H2MockingApplicationContextInitializer.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.marklogic.sample.h2; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; -import org.springframework.core.io.FileSystemResource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.ResourcePropertySource; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; -import org.springframework.mock.env.MockEnvironment; - -import java.io.IOException; - -public class H2MockingApplicationContextInitializer implements ApplicationContextInitializer { - - private Logger logger = LoggerFactory.getLogger(this.getClass()); - - public static final String DEFAULT_APPLICATION_PROPERTIES = "job.properties"; - - @Override - public void initialize(ConfigurableApplicationContext applicationContext) { - MockEnvironment env = new MockEnvironment(); - final MutablePropertySources propertySources = env.getPropertySources(); - //propertySources.addFirst(getApplicationProperties(env)); - env.setProperty("jdbc_driver", "org.h2.Driver"); - env.setProperty("jdbc_username", "sa"); - env.setProperty("jdbc_url", "jdbc:h2:mem:testdb"); - env.setProperty("chunk", "5"); - applicationContext.setEnvironment(env); - - } -/* - protected PropertySource getApplicationProperties(final ConfigurableEnvironment environment) { - try { - final Resource propertiesResource = new FileSystemResource(DEFAULT_APPLICATION_PROPERTIES); - final ResourcePropertySource propertySource = new ResourcePropertySource(propertiesResource); - logger.info("Configured application properties from: {}", propertySource); - return propertySource; - } catch (final IOException ex) { - throw new RuntimeException("Unable to load application properties from default location: " - + DEFAULT_APPLICATION_PROPERTIES, ex); - } - } - */ -} From 24cec47197cc23a323dfb6a4f826cdf5efd86c7f Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 12:03:05 -0400 Subject: [PATCH 10/15] Update README --- examples/spring-batch/README.md | 43 +++++++++++++-------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/examples/spring-batch/README.md b/examples/spring-batch/README.md index 65fcf7f77f..bba3b11f29 100644 --- a/examples/spring-batch/README.md +++ b/examples/spring-batch/README.md @@ -4,26 +4,23 @@ This example demonstrates how to run a custom Spring Batch job against the Data Learning [Spring Batch](http://docs.spring.io/spring-batch/reference/html/spring-batch-intro.html) is beyond the scope of this README. But let's pretend you know enough to be dangerous. -Now you want to use Spring Batch to load a bunch of data into MarkLogic. Maybe that data is coming from a message queue. Maybe it's a bunch of files in a folder. It can be anything really. +Now you want to use Spring Batch to load a bunch from a relational database into MarkLogic. ## What's the Big Idea? -The idea is pretty simple. You read the data, do a little processing (maybe), and then write it into MarkLogic. But to properly integrate with the Data Hub Framework you will want to run your data through an [input flow](https://github.com/marklogic-community/marklogic-data-hub/wiki/The-MarkLogic-Data-Hub-Overview#ingest). +The idea is pretty simple. You read the data into a tabular format using a SQL query (SELECT * FROM TABLE), transform the row into an XML document, and then write it into MarkLogic. But to properly integrate with the Data Hub Framework you need to run your data through an [input flow](https://github.com/marklogic-community/marklogic-data-hub/wiki/The-MarkLogic-Data-Hub-Overview#ingest). The MarkLogic Spring Batch project provides an interface called the DataHubItemWriter that runs the appropriate input flow. ## How does it work? -This example includes a sample Spring Boot Configuration [LoadAndRunFlow.java](https://github.com/marklogic-community/marklogic-data-hub/blob/develop/examples/spring-batch/src/main/java/example/LoadAndRunFlow.java) that configures a job to ingest some xml files and run a flow. +This example includes a sample Spring Boot Configuration [SqlDbToHubJobConfig.java](https://github.com/marklogic-community/marklogic-data-hub/blob/develop/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java) that configures a job. -This example depends on a runtime class **com.marklogic.spring.batch.hub.HubJobRunner** that is responsible for reading command line parameters and connects to the Data Hub by reading your gradle project files. +This example depends on a properties file called job.properties. This project provides a sample job.properties file but you may need to change the host or port numbers for your environment. ## How do I Run this Example? -First you compile it. +`gradle ingestInvoices` -`gradle installDist` - -Then you launch it. - -`./run.sh` +This reads invoice, customer, item, and customer data from a relational database called H2. The data can be viewed in H2 by calling the following command. +`gradle runH2` ## How do I add this to my existing Data Hub Project? @@ -42,26 +39,20 @@ dependencies { // existing dependencies // add this one: - compile "com.marklogic:marklogic-spring-batch-core:0.6.0" + compile "com.marklogic:marklogic-spring-batch-core:1.0.1" } - -// add the distributions section -distributions { - main { - baseName = 'baseJob' - } +task ingestInvoices(type: JavaExec) { + classpath = sourceSets.main.runtimeClasspath + main = "com.marklogic.spring.batch.core.launch.support.CommandLineJobRunner" + args = [ + "--job_path", "com.marklogic.hub.job.MigrateInvoicesConfiguration", + "--job_id", "job", + "--entity", "Invoice", + "--flow", "ingest-invoice-db" + ] } -// add the mainClassName to specify the HubJobRunner -mainClassName = "com.marklogic.spring.batch.hub.HubJobRunner" ``` -Then drop in your custom Java Config class in src/main/java/..... - -Next you simply Compile your code with `gradle installDist`. - -Then you can run take a look at the [run.sh script](https://github.com/marklogic-community/marklogic-data-hub/blob/develop/examples/spring-batch/run.sh) to see how to run your custom config. - -Note that this is not the only way to run it. It's merely the easiest. Java Ninjas can directly call the main() function of the [HubJobRunner class](https://github.com/marklogic-community/marklogic-data-hub/blob/develop/marklogic-data-hub/src/main/java/com/marklogic/spring/batch/hub/HubJobRunner.java). Or you can make your own class to start up Spring Batch by reading the HubJobRunner code and doing something similar. From efff835f945654048b3e0d68f9dacd9db38d2087 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 12:03:33 -0400 Subject: [PATCH 11/15] Remove old example --- .../src/main/java/example/LoadAndRunFlow.java | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 examples/spring-batch/src/main/java/example/LoadAndRunFlow.java diff --git a/examples/spring-batch/src/main/java/example/LoadAndRunFlow.java b/examples/spring-batch/src/main/java/example/LoadAndRunFlow.java deleted file mode 100644 index 3c7f353fc4..0000000000 --- a/examples/spring-batch/src/main/java/example/LoadAndRunFlow.java +++ /dev/null @@ -1,87 +0,0 @@ -package example; - -import com.marklogic.client.document.DocumentWriteOperation; -import com.marklogic.client.document.DocumentWriteSet; -import com.marklogic.client.document.GenericDocumentManager; -import com.marklogic.client.document.ServerTransform; -import com.marklogic.client.helper.DatabaseClientProvider; -import com.marklogic.spring.batch.item.processor.ResourceToDocumentWriteOperationItemProcessor; -import com.marklogic.spring.batch.item.reader.EnhancedResourcesItemReader; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; -import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; -import org.springframework.batch.core.configuration.annotation.JobScope; -import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; -import org.springframework.batch.item.ItemProcessor; -import org.springframework.batch.item.ItemWriter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.EnvironmentAware; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.core.env.Environment; -import org.springframework.core.io.Resource; -import com.marklogic.hub.HubConfig; - -import java.util.*; - -@EnableBatchProcessing -public class LoadAndRunFlow implements EnvironmentAware { - - private Environment env; - - private final String JOB_NAME = "yourJob"; - - @Bean - public Job job(JobBuilderFactory jobBuilderFactory, Step step) { - return jobBuilderFactory.get(JOB_NAME).start(step).build(); - } - - @Bean - @JobScope - public Step step( - StepBuilderFactory stepBuilderFactory, - DatabaseClientProvider databaseClientProvider, - @Value("#{jobParameters['project_dir']}") String projectDir, - @Value("#{jobParameters['input_file_path']}") String inputFilePath, - @Value("#{jobParameters['input_file_pattern']}") String inputFilePattern, - @Value("#{jobParameters['entity_name']}") String entityName, - @Value("#{jobParameters['flow_name']}") String flowName, - @Value("#{jobParameters['output_collections']}") String[] collections) { - - HubConfig hubConfig = HubConfig.hubFromEnvironment(projectDir, "local"); - - GenericDocumentManager docMgr = hubConfig.newStagingClient().newDocumentManager(); - - ItemProcessor processor = new ResourceToDocumentWriteOperationItemProcessor(); - ItemWriter writer = new ItemWriter() { - @Override - public void write(List items) throws Exception { - DocumentWriteSet batch = docMgr.newWriteSet(); - for (DocumentWriteOperation item : items) { - String uri = item.getUri(); - batch.add(uri, item.getMetadata(), item.getContent()); - } - ServerTransform runFlow = new ServerTransform("run-flow"); - runFlow.addParameter("job-id", UUID.randomUUID().toString()); - runFlow.addParameter("entity", entityName); - runFlow.addParameter("flow", flowName); - docMgr.write(batch, runFlow); - } - }; - - return stepBuilderFactory.get("step1") - .chunk(10) - .reader(new EnhancedResourcesItemReader(inputFilePath, inputFilePattern)) - .processor(processor) - .writer(writer) - .build(); - } - - - @Override - public void setEnvironment(Environment environment) { - this.env = environment; - } - -} From 1946e33bf77cf413b3dd38a405c6ab66d1e76b25 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 12:04:00 -0400 Subject: [PATCH 12/15] Remove old script --- examples/spring-batch/run.sh | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100755 examples/spring-batch/run.sh diff --git a/examples/spring-batch/run.sh b/examples/spring-batch/run.sh deleted file mode 100755 index 5815f50d0c..0000000000 --- a/examples/spring-batch/run.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -PWD=`pwd` - -build/install/baseJob/bin/hubBatch \ - --config example.LoadAndRunFlow \ - --project_dir ${PWD} \ - --env local \ - --input_file_path ${PWD}/input \ - --input_file_pattern .*\.xml \ - --entity_name Monster \ - --flow_name ingest-monster \ - --chunk 100 From d0b0321c4475ff588f3406d3dcf6ff3eca19ee27 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 12:04:23 -0400 Subject: [PATCH 13/15] Remove old data files --- examples/spring-batch/input/bigbird.xml | 5 ----- examples/spring-batch/input/elmo.xml | 5 ----- examples/spring-batch/input/grover.xml | 5 ----- 3 files changed, 15 deletions(-) delete mode 100644 examples/spring-batch/input/bigbird.xml delete mode 100644 examples/spring-batch/input/elmo.xml delete mode 100644 examples/spring-batch/input/grover.xml diff --git a/examples/spring-batch/input/bigbird.xml b/examples/spring-batch/input/bigbird.xml deleted file mode 100644 index 0641ea7e29..0000000000 --- a/examples/spring-batch/input/bigbird.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - BigBird - yellow - diff --git a/examples/spring-batch/input/elmo.xml b/examples/spring-batch/input/elmo.xml deleted file mode 100644 index 633a9099e9..0000000000 --- a/examples/spring-batch/input/elmo.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Elmo - red - diff --git a/examples/spring-batch/input/grover.xml b/examples/spring-batch/input/grover.xml deleted file mode 100644 index cdc0ff8f16..0000000000 --- a/examples/spring-batch/input/grover.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Grover - blue - From 820d79c44fab4973cca78ec5079e370fb49d5574 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 16:04:10 -0400 Subject: [PATCH 14/15] Reworded readme and put in reference to E/R diagram --- examples/spring-batch/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/spring-batch/README.md b/examples/spring-batch/README.md index bba3b11f29..272f7d5fe3 100644 --- a/examples/spring-batch/README.md +++ b/examples/spring-batch/README.md @@ -4,13 +4,13 @@ This example demonstrates how to run a custom Spring Batch job against the Data Learning [Spring Batch](http://docs.spring.io/spring-batch/reference/html/spring-batch-intro.html) is beyond the scope of this README. But let's pretend you know enough to be dangerous. -Now you want to use Spring Batch to load a bunch from a relational database into MarkLogic. +This example loads data from the [Invoice Database](./invoices-sql-diagram.jpg) to use Spring Batch to load from a relational database into MarkLogic. ## What's the Big Idea? The idea is pretty simple. You read the data into a tabular format using a SQL query (SELECT * FROM TABLE), transform the row into an XML document, and then write it into MarkLogic. But to properly integrate with the Data Hub Framework you need to run your data through an [input flow](https://github.com/marklogic-community/marklogic-data-hub/wiki/The-MarkLogic-Data-Hub-Overview#ingest). The MarkLogic Spring Batch project provides an interface called the DataHubItemWriter that runs the appropriate input flow. ## How does it work? -This example includes a sample Spring Boot Configuration [SqlDbToHubJobConfig.java](https://github.com/marklogic-community/marklogic-data-hub/blob/develop/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java) that configures a job. +This example includes a sample Spring Batch Configuration [SqlDbToHubJobConfig.java](https://github.com/marklogic-community/marklogic-data-hub/blob/develop/examples/spring-batch/src/main/java/com/marklogic/hub/job/SqlDbToHubJobConfig.java) that configures a job. To execute the job, we are utilizing the CommandLineJobRunner from the MarkLogic Spring Batch project. This example depends on a properties file called job.properties. This project provides a sample job.properties file but you may need to change the host or port numbers for your environment. From bcbe6236c34316bb28f398285fa11a633e0a08d0 Mon Sep 17 00:00:00 2001 From: Scott Stafford Date: Mon, 7 Aug 2017 16:07:05 -0400 Subject: [PATCH 15/15] Update default host to localhost, update readme to deploy the ML Job Repo --- examples/spring-batch/README.md | 3 +++ examples/spring-batch/job.properties | 2 +- examples/spring-batch/src/test/resources/job.properties | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/spring-batch/README.md b/examples/spring-batch/README.md index 272f7d5fe3..a4b421afe6 100644 --- a/examples/spring-batch/README.md +++ b/examples/spring-batch/README.md @@ -16,6 +16,9 @@ This example depends on a properties file called job.properties. This project p ## How do I Run this Example? +First [deploy the MarkLogic Job Repository](https://github.com/marklogic-community/marklogic-spring-batch/wiki/MarkLogicJobRepository). When the Spring Batch application starts it needs to persist the job into MarkLogic. +Once your datahub is up and running and your job.properties file is configured based on the earlier step, then just execute the job with the following gradle command. + `gradle ingestInvoices` This reads invoice, customer, item, and customer data from a relational database called H2. The data can be viewed in H2 by calling the following command. diff --git a/examples/spring-batch/job.properties b/examples/spring-batch/job.properties index 4d4e62ae52..7242841a24 100644 --- a/examples/spring-batch/job.properties +++ b/examples/spring-batch/job.properties @@ -1,4 +1,4 @@ -marklogic.host=oscar +marklogic.host=localhost marklogic.port=8010 marklogic.username=admin marklogic.password=admin diff --git a/examples/spring-batch/src/test/resources/job.properties b/examples/spring-batch/src/test/resources/job.properties index eb629ef5a4..9248328c97 100644 --- a/examples/spring-batch/src/test/resources/job.properties +++ b/examples/spring-batch/src/test/resources/job.properties @@ -1,4 +1,4 @@ -marklogic.host=oscar +marklogic.host=localhost marklogic.port=8010 marklogic.username=admin marklogic.password=admin