Add module for Neo4j

This initial import is a copy of the code
from Spring Batch v4.3.3.
This commit is contained in:
Mahmoud Ben Hassine
2021-09-01 10:03:38 +02:00
parent e1d5fc5a4a
commit edae1738c5
12 changed files with 1649 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
name: Spring Batch Neo4j
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Maven
run: mvn -B package --file pom.xml
working-directory: spring-batch-neo4j

View File

@@ -11,6 +11,7 @@ This project is part of the [Spring organization][] on GitHub.
| spring-batch-excel | Support for [Microsoft Excel] | [@mdeinum](https://github.com/mdeinum) | 0.1.0 | [![Spring Batch Excel](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-excel.yml/badge.svg)](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-excel.yml) |
| spring-batch-elasticsearch | Support for [Elasticsearch] | TBA | 0.1.0-SNAPSHOT | [![Spring Batch Elasticsearch](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-elasticsearch.yml/badge.svg)](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-elasticsearch.yml) |
| spring-batch-bigquery | Support for [Google BigQuery] | [@dgray16](https://github.com/dgray16) | 0.1.0-SNAPSHOT | [![Spring Batch BigQuery](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-bigquery.yml/badge.svg)](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-bigquery.yml) |
| spring-batch-neo4j | Support for [Neo4j] | [@michael-simons](https://github.com/michael-simons) | 0.1.0-SNAPSHOT | [![Spring Batch Neo4j](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-neo4j.yml/badge.svg)](https://github.com/spring-projects/spring-batch-extensions/actions/workflows/spring-batch-neo4j.yml) |
## Getting support
@@ -87,6 +88,8 @@ Follow the Spring Batch team members and contributors on Twitter:
* [@michaelminella](https://twitter.com/michaelminella) - Michael Minella
* [@b_e_n_a_s](https://twitter.com/b_e_n_a_s) - Mahmoud Ben Hassine
* [@mdeinum](https://twitter.com/mdeinum) - Marten Deinum
* [@rotnroll666](https://twitter.com/rotnroll666) - Michael Simons
* [@meistermeier](https://twitter.com/meistermeier) - Gerrit Meier
## License
@@ -100,6 +103,7 @@ noted differently for individual extension Modules, but this should be the rare
[Microsoft Excel]: https://www.microsoft.com/en-us/microsoft-365/excel
[Elasticsearch]: https://www.elastic.co
[Google BigQuery]: https://cloud.google.com/bigquery
[Neo4j]: https://neo4j.com
[spring-batch tag]: https://stackoverflow.com/questions/tagged/spring-batch
[Spring Batch]: https://github.com/spring-projects/spring-batch
[Spring Boot]: https://github.com/spring-projects/spring-boot

View File

@@ -0,0 +1,31 @@
# spring-batch-neo4j
This extension contains an `ItemReader` and `ItemWriter` implementations for [Neo4j](https://neo4j.com).
# Usage example
The `Neo4jItemReader` can be configured as follows:
```java
SessionFactory sessionFactory = ...
Neo4jItemReader<String> itemReader = new Neo4jItemReaderBuilder<String>()
.sessionFactory(sessionFactory)
.name("itemReader")
.targetType(String.class)
.startStatement("n=node(*)")
.orderByStatement("n.age")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m")
.pageSize(50)
.build();
```
The `Neo4jItemWriter` can be configured as follows:
```java
SessionFactory sessionFactory = ...
Neo4jItemWriter<String> writer = new Neo4jItemWriterBuilder<String>()
.sessionFactory(sessionFactory)
.build();
```

157
spring-batch-neo4j/pom.xml Normal file
View File

@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2021 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.batch.extensions</groupId>
<artifactId>spring-batch-neo4j</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>Spring Batch Neo4j</name>
<description>Spring Batch extension for Neo4j</description>
<url>https://github.com/spring-projects/spring-batch-extensions/tree/main/spring-batch-neo4j</url>
<licenses>
<license>
<name>Apache 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>https://github.com/spring-projects/spring-batch-extensions</url>
<connection>git://github.com/spring-projects/spring-batch-extensions.git</connection>
<developerConnection>git@github.com:spring-projects/spring-batch-extensions.git</developerConnection>
</scm>
<developers>
<developer>
<id>michael-simons</id>
<name>Michael Simons</name>
<url>https://github.com/michael-simons</url>
</developer>
<developer>
<id>meistermeier</id>
<name>Gerrit Meier</name>
<url>https://github.com/meistermeier</url>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- Production dependencies-->
<spring.batch.version>4.3.3</spring.batch.version>
<neo4j-ogm-core.version>3.2.21</neo4j-ogm-core.version>
<!-- Test Dependencies -->
<assertj.version>3.18.1</assertj.version>
<junit.version>4.13.2</junit.version>
<mockito.version>3.6.0</mockito.version>
<!-- Maven plugins -->
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-javadoc-plugin.version>3.2.0</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-infrastructure</artifactId>
<version>${spring.batch.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-core</artifactId>
<version>${neo4j-ogm-core.version}</version>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgs>
<arg>-Xlint:all,deprecation</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.data.AbstractPaginatedDataItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* <p>
* Restartable {@link ItemReader} that reads objects from the graph database Neo4j
* via a paging technique.
* </p>
*
* <p>
* It executes cypher queries built from the statement fragments provided to
* retrieve the requested data. The query is executed using paged requests of
* a size specified in {@link #setPageSize(int)}. Additional pages are requested
* as needed when the {@link #read()} method is called. On restart, the reader
* will begin again at the same number item it left off at.
* </p>
*
* <p>
* Performance is dependent on your Neo4J configuration (embedded or remote) as
* well as page size. Setting a fairly large page size and using a commit
* interval that matches the page size should provide better performance.
* </p>
*
* <p>
* This implementation is thread-safe between calls to
* {@link #open(org.springframework.batch.item.ExecutionContext)}, however you
* should set <code>saveState=false</code> if used in a multi-threaded
* environment (no restart available).
* </p>
*
* @author Michael Minella
* @author Mahmoud Ben Hassine
*/
public class Neo4jItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {
protected Log logger = LogFactory.getLog(getClass());
private SessionFactory sessionFactory;
private String startStatement;
private String returnStatement;
private String matchStatement;
private String whereStatement;
private String orderByStatement;
private Class<T> targetType;
private Map<String, Object> parameterValues;
/**
* Optional parameters to be used in the cypher query.
*
* @param parameterValues the parameter values to be used in the cypher query
*/
public void setParameterValues(Map<String, Object> parameterValues) {
this.parameterValues = parameterValues;
}
protected final Map<String, Object> getParameterValues() {
return this.parameterValues;
}
/**
* The start segment of the cypher query. START is prepended
* to the statement provided and should <em>not</em> be
* included.
*
* @param startStatement the start fragment of the cypher query.
*/
public void setStartStatement(String startStatement) {
this.startStatement = startStatement;
}
/**
* The return statement of the cypher query. RETURN is prepended
* to the statement provided and should <em>not</em> be
* included
*
* @param returnStatement the return fragment of the cypher query.
*/
public void setReturnStatement(String returnStatement) {
this.returnStatement = returnStatement;
}
/**
* An optional match fragment of the cypher query. MATCH is
* prepended to the statement provided and should <em>not</em>
* be included.
*
* @param matchStatement the match fragment of the cypher query
*/
public void setMatchStatement(String matchStatement) {
this.matchStatement = matchStatement;
}
/**
* An optional where fragment of the cypher query. WHERE is
* prepended to the statement provided and should <em>not</em>
* be included.
*
* @param whereStatement where fragment of the cypher query
*/
public void setWhereStatement(String whereStatement) {
this.whereStatement = whereStatement;
}
/**
* A list of properties to order the results by. This is
* required so that subsequent page requests pull back the
* segment of results correctly. ORDER BY is prepended to
* the statement provided and should <em>not</em> be included.
*
* @param orderByStatement order by fragment of the cypher query.
*/
public void setOrderByStatement(String orderByStatement) {
this.orderByStatement = orderByStatement;
}
protected SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* Establish the session factory for the reader.
* @param sessionFactory the factory to use for the reader.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* The object type to be returned from each call to {@link #read()}
*
* @param targetType the type of object to return.
*/
public void setTargetType(Class<T> targetType) {
this.targetType = targetType;
}
protected final Class<T> getTargetType() {
return this.targetType;
}
protected String generateLimitCypherQuery() {
StringBuilder query = new StringBuilder(128);
query.append("START ").append(startStatement);
query.append(matchStatement != null ? " MATCH " + matchStatement : "");
query.append(whereStatement != null ? " WHERE " + whereStatement : "");
query.append(" RETURN ").append(returnStatement);
query.append(" ORDER BY ").append(orderByStatement);
query.append(" SKIP " + (pageSize * page));
query.append(" LIMIT " + pageSize);
String resultingQuery = query.toString();
if (logger.isDebugEnabled()) {
logger.debug(resultingQuery);
}
return resultingQuery;
}
/**
* Checks mandatory properties
*
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(sessionFactory != null,"A SessionFactory is required");
Assert.state(targetType != null, "The type to be returned is required");
Assert.state(StringUtils.hasText(startStatement), "A START statement is required");
Assert.state(StringUtils.hasText(returnStatement), "A RETURN statement is required");
Assert.state(StringUtils.hasText(orderByStatement), "A ORDER BY statement is required");
}
@SuppressWarnings("unchecked")
@Override
protected Iterator<T> doPageRead() {
Session session = getSessionFactory().openSession();
Iterable<T> queryResults = session.query(getTargetType(),
generateLimitCypherQuery(),
getParameterValues());
if(queryResults != null) {
return queryResults.iterator();
}
else {
return new ArrayList<T>().iterator();
}
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* <p>
* A {@link ItemWriter} implementation that writes to a Neo4j database.
* </p>
*
* <p>
* This writer is thread-safe once all properties are set (normal singleton
* behavior) so it can be used in multiple concurrent transactions.
* </p>
*
* @author Michael Minella
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
*
*/
public class Neo4jItemWriter<T> implements ItemWriter<T>, InitializingBean {
protected static final Log logger = LogFactory
.getLog(Neo4jItemWriter.class);
private boolean delete = false;
private SessionFactory sessionFactory;
/**
* Boolean flag indicating whether the writer should save or delete the item at write
* time.
* @param delete true if write should delete item, false if item should be saved.
* Default is false.
*/
public void setDelete(boolean delete) {
this.delete = delete;
}
/**
* Establish the session factory that will be used to create {@link Session} instances
* for interacting with Neo4j.
* @param sessionFactory sessionFactory to be used.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Checks mandatory properties
*
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(this.sessionFactory != null,
"A SessionFactory is required");
}
/**
* Write all items to the data store.
*
* @see org.springframework.batch.item.ItemWriter#write(java.util.List)
*/
@Override
public void write(List<? extends T> items) throws Exception {
if(!CollectionUtils.isEmpty(items)) {
doWrite(items);
}
}
/**
* Performs the actual write using the template. This can be overridden by
* a subclass if necessary.
*
* @param items the list of items to be persisted.
*/
protected void doWrite(List<? extends T> items) {
if(delete) {
delete(items);
}
else {
save(items);
}
}
private void delete(List<? extends T> items) {
Session session = this.sessionFactory.openSession();
for(T item : items) {
session.delete(item);
}
}
private void save(List<? extends T> items) {
Session session = this.sessionFactory.openSession();
for (T item : items) {
session.save(item);
}
}
}

View File

@@ -0,0 +1,273 @@
/*
* Copyright 2017-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j.builder;
import java.util.Map;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.data.Neo4jItemReader;
import org.springframework.util.Assert;
/**
* A builder for the {@link Neo4jItemReader}.
*
* @author Glenn Renfro
* @see Neo4jItemReader
*/
public class Neo4jItemReaderBuilder<T> {
private SessionFactory sessionFactory;
private String startStatement;
private String returnStatement;
private String matchStatement;
private String whereStatement;
private String orderByStatement;
private Class<T> targetType;
private Map<String, Object> parameterValues;
private int pageSize = 10;
private boolean saveState = true;
private String name;
private int maxItemCount = Integer.MAX_VALUE;
private int currentItemCount;
/**
* Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport}
* should be persisted within the {@link org.springframework.batch.item.ExecutionContext}
* for restart purposes.
*
* @param saveState defaults to true
* @return The current instance of the builder.
*/
public Neo4jItemReaderBuilder<T> saveState(boolean saveState) {
this.saveState = saveState;
return this;
}
/**
* The name used to calculate the key within the
* {@link org.springframework.batch.item.ExecutionContext}. Required if
* {@link #saveState(boolean)} is set to true.
*
* @param name name of the reader instance
* @return The current instance of the builder.
* @see org.springframework.batch.item.ItemStreamSupport#setName(String)
*/
public Neo4jItemReaderBuilder<T> name(String name) {
this.name = name;
return this;
}
/**
* Configure the max number of items to be read.
*
* @param maxItemCount the max items to be read
* @return The current instance of the builder.
* @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int)
*/
public Neo4jItemReaderBuilder<T> maxItemCount(int maxItemCount) {
this.maxItemCount = maxItemCount;
return this;
}
/**
* Index for the current item. Used on restarts to indicate where to start from.
*
* @param currentItemCount current index
* @return this instance for method chaining
* @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int)
*/
public Neo4jItemReaderBuilder<T> currentItemCount(int currentItemCount) {
this.currentItemCount = currentItemCount;
return this;
}
/**
* Establish the session factory for the reader.
* @param sessionFactory the factory to use for the reader.
* @return this instance for method chaining
* @see Neo4jItemReader#setSessionFactory(SessionFactory)
*/
public Neo4jItemReaderBuilder<T> sessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
return this;
}
/**
* The number of items to be read with each page.
*
* @param pageSize the number of items
* @return this instance for method chaining
* @see Neo4jItemReader#setPageSize(int)
*/
public Neo4jItemReaderBuilder<T> pageSize(int pageSize) {
this.pageSize = pageSize;
return this;
}
/**
* Optional parameters to be used in the cypher query.
*
* @param parameterValues the parameter values to be used in the cypher query
* @return this instance for method chaining
* @see Neo4jItemReader#setParameterValues(Map)
*/
public Neo4jItemReaderBuilder<T> parameterValues(Map<String, Object> parameterValues) {
this.parameterValues = parameterValues;
return this;
}
/**
* The start segment of the cypher query. START is prepended to the statement provided
* and should <em>not</em> be included.
*
* @param startStatement the start fragment of the cypher query.
* @return this instance for method chaining
* @see Neo4jItemReader#setStartStatement(String)
*/
public Neo4jItemReaderBuilder<T> startStatement(String startStatement) {
this.startStatement = startStatement;
return this;
}
/**
* The return statement of the cypher query. RETURN is prepended to the statement
* provided and should <em>not</em> be included
*
* @param returnStatement the return fragment of the cypher query.
* @return this instance for method chaining
* @see Neo4jItemReader#setReturnStatement(String)
*/
public Neo4jItemReaderBuilder<T> returnStatement(String returnStatement) {
this.returnStatement = returnStatement;
return this;
}
/**
* An optional match fragment of the cypher query. MATCH is prepended to the statement
* provided and should <em>not</em> be included.
*
* @param matchStatement the match fragment of the cypher query
* @return this instance for method chaining
* @see Neo4jItemReader#setMatchStatement(String)
*/
public Neo4jItemReaderBuilder<T> matchStatement(String matchStatement) {
this.matchStatement = matchStatement;
return this;
}
/**
* An optional where fragment of the cypher query. WHERE is prepended to the statement
* provided and should <em>not</em> be included.
*
* @param whereStatement where fragment of the cypher query
* @return this instance for method chaining
* @see Neo4jItemReader#setWhereStatement(String)
*/
public Neo4jItemReaderBuilder<T> whereStatement(String whereStatement) {
this.whereStatement = whereStatement;
return this;
}
/**
* A list of properties to order the results by. This is required so that subsequent
* page requests pull back the segment of results correctly. ORDER BY is prepended to
* the statement provided and should <em>not</em> be included.
*
* @param orderByStatement order by fragment of the cypher query.
* @return this instance for method chaining
* @see Neo4jItemReader#setOrderByStatement(String)
*/
public Neo4jItemReaderBuilder<T> orderByStatement(String orderByStatement) {
this.orderByStatement = orderByStatement;
return this;
}
/**
* The object type to be returned from each call to {@link Neo4jItemReader#read()}
*
* @param targetType the type of object to return.
* @return this instance for method chaining
* @see Neo4jItemReader#setTargetType(Class)
*/
public Neo4jItemReaderBuilder<T> targetType(Class<T> targetType) {
this.targetType = targetType;
return this;
}
/**
* Returns a fully constructed {@link Neo4jItemReader}.
*
* @return a new {@link Neo4jItemReader}
*/
public Neo4jItemReader<T> build() {
if (this.saveState) {
Assert.hasText(this.name, "A name is required when saveState is set to true");
}
Assert.notNull(this.sessionFactory, "sessionFactory is required.");
Assert.notNull(this.targetType, "targetType is required.");
Assert.hasText(this.startStatement, "startStatement is required.");
Assert.hasText(this.returnStatement, "returnStatement is required.");
Assert.hasText(this.orderByStatement, "orderByStatement is required.");
Assert.isTrue(this.pageSize > 0, "pageSize must be greater than zero");
Assert.isTrue(this.maxItemCount > 0, "maxItemCount must be greater than zero");
Assert.isTrue(this.maxItemCount > this.currentItemCount , "maxItemCount must be greater than currentItemCount");
Neo4jItemReader<T> reader = new Neo4jItemReader<>();
reader.setMatchStatement(this.matchStatement);
reader.setOrderByStatement(this.orderByStatement);
reader.setPageSize(this.pageSize);
reader.setParameterValues(this.parameterValues);
reader.setSessionFactory(this.sessionFactory);
reader.setTargetType(this.targetType);
reader.setStartStatement(this.startStatement);
reader.setReturnStatement(this.returnStatement);
reader.setWhereStatement(this.whereStatement);
reader.setName(this.name);
reader.setSaveState(this.saveState);
reader.setCurrentItemCount(this.currentItemCount);
reader.setMaxItemCount(this.maxItemCount);
return reader;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2017-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j.builder;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.data.Neo4jItemWriter;
import org.springframework.util.Assert;
/**
* A builder implementation for the {@link Neo4jItemWriter}
*
* @author Glenn Renfro
* @see Neo4jItemWriter
*/
public class Neo4jItemWriterBuilder<T> {
private boolean delete = false;
private SessionFactory sessionFactory;
/**
* Boolean flag indicating whether the writer should save or delete the item at write
* time.
* @param delete true if write should delete item, false if item should be saved.
* Default is false.
* @return The current instance of the builder
* @see Neo4jItemWriter#setDelete(boolean)
*/
public Neo4jItemWriterBuilder<T> delete(boolean delete) {
this.delete = delete;
return this;
}
/**
* Establish the session factory that will be used to create {@link Session} instances
* for interacting with Neo4j.
* @param sessionFactory sessionFactory to be used.
* @return The current instance of the builder
* @see Neo4jItemWriter#setSessionFactory(SessionFactory)
*/
public Neo4jItemWriterBuilder<T> sessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
return this;
}
/**
* Validates and builds a {@link org.springframework.batch.item.data.Neo4jItemWriter}.
*
* @return a {@link Neo4jItemWriter}
*/
public Neo4jItemWriter<T> build() {
Assert.notNull(sessionFactory, "sessionFactory is required.");
Neo4jItemWriter<T> writer = new Neo4jItemWriter<>();
writer.setDelete(this.delete);
writer.setSessionFactory(this.sessionFactory);
return writer;
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.when;
public class Neo4jItemReaderTests {
@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
@Mock
private Iterable<String> result;
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
private Neo4jItemReader<String> buildSessionBasedReader() throws Exception {
Neo4jItemReader<String> reader = new Neo4jItemReader<>();
reader.setSessionFactory(this.sessionFactory);
reader.setTargetType(String.class);
reader.setStartStatement("n=node(*)");
reader.setReturnStatement("*");
reader.setOrderByStatement("n.age");
reader.setPageSize(50);
reader.afterPropertiesSet();
return reader;
}
@Test
public void testAfterPropertiesSet() throws Exception {
Neo4jItemReader<String> reader = new Neo4jItemReader<>();
try {
reader.afterPropertiesSet();
fail("SessionFactory was not set but exception was not thrown.");
} catch (IllegalStateException iae) {
assertEquals("A SessionFactory is required", iae.getMessage());
} catch (Throwable t) {
fail("Wrong exception was thrown:" + t);
}
reader.setSessionFactory(this.sessionFactory);
try {
reader.afterPropertiesSet();
fail("Target Type was not set but exception was not thrown.");
} catch (IllegalStateException iae) {
assertEquals("The type to be returned is required", iae.getMessage());
} catch (Throwable t) {
fail("Wrong exception was thrown:" + t);
}
reader.setTargetType(String.class);
try {
reader.afterPropertiesSet();
fail("START was not set but exception was not thrown.");
} catch (IllegalStateException iae) {
assertEquals("A START statement is required", iae.getMessage());
} catch (Throwable t) {
fail("Wrong exception was thrown:" + t);
}
reader.setStartStatement("n=node(*)");
try {
reader.afterPropertiesSet();
fail("RETURN was not set but exception was not thrown.");
} catch (IllegalStateException iae) {
assertEquals("A RETURN statement is required", iae.getMessage());
} catch (Throwable t) {
fail("Wrong exception was thrown:" + t);
}
reader.setReturnStatement("n.name, n.phone");
try {
reader.afterPropertiesSet();
fail("ORDER BY was not set but exception was not thrown.");
} catch (IllegalStateException iae) {
assertEquals("A ORDER BY statement is required", iae.getMessage());
} catch (Throwable t) {
fail("Wrong exception was thrown:" + t);
}
reader.setOrderByStatement("n.age");
reader.afterPropertiesSet();
reader = new Neo4jItemReader<>();
reader.setSessionFactory(this.sessionFactory);
reader.setTargetType(String.class);
reader.setStartStatement("n=node(*)");
reader.setReturnStatement("n.name, n.phone");
reader.setOrderByStatement("n.age");
reader.afterPropertiesSet();
}
@SuppressWarnings("unchecked")
@Test
public void testNullResultsWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
ArgumentCaptor<String> query = ArgumentCaptor.forClass(String.class);
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(eq(String.class), query.capture(), isNull())).thenReturn(null);
assertFalse(itemReader.doPageRead().hasNext());
assertEquals("START n=node(*) RETURN * ORDER BY n.age SKIP 0 LIMIT 50", query.getValue());
}
@SuppressWarnings("unchecked")
@Test
public void testNoResultsWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
ArgumentCaptor<String> query = ArgumentCaptor.forClass(String.class);
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(eq(String.class), query.capture(), isNull())).thenReturn(result);
when(result.iterator()).thenReturn(Collections.emptyIterator());
assertFalse(itemReader.doPageRead().hasNext());
assertEquals("START n=node(*) RETURN * ORDER BY n.age SKIP 0 LIMIT 50", query.getValue());
}
@SuppressWarnings("serial")
@Test
public void testResultsWithMatchAndWhereWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
itemReader.setMatchStatement("n -- m");
itemReader.setWhereStatement("has(n.name)");
itemReader.setReturnStatement("m");
itemReader.afterPropertiesSet();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class, "START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", null)).thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertTrue(itemReader.doPageRead().hasNext());
}
@SuppressWarnings("serial")
@Test
public void testResultsWithMatchAndWhereWithParametersWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
Map<String, Object> params = new HashMap<>();
params.put("foo", "bar");
itemReader.setParameterValues(params);
itemReader.setMatchStatement("n -- m");
itemReader.setWhereStatement("has(n.name)");
itemReader.setReturnStatement("m");
itemReader.afterPropertiesSet();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class, "START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", params)).thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertTrue(itemReader.doPageRead().hasNext());
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j;
import java.util.ArrayList;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
public class Neo4jItemWriterTests {
@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
private Neo4jItemWriter<String> writer;
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
@Test
public void testAfterPropertiesSet() throws Exception{
writer = new Neo4jItemWriter<>();
try {
writer.afterPropertiesSet();
fail("SessionFactory was not set but exception was not thrown.");
} catch (IllegalStateException iae) {
assertEquals("A SessionFactory is required", iae.getMessage());
} catch (Throwable t) {
fail("Wrong exception was thrown.");
}
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
}
@Test
public void testWriteNullSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
writer.write(null);
verifyNoInteractions(this.session);
}
@Test
public void testWriteNullWithSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(null);
verifyNoInteractions(this.session);
}
@Test
public void testWriteNoItemsWithSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(new ArrayList<>());
verifyNoInteractions(this.session);
}
@Test
public void testWriteItemsWithSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
List<String> items = new ArrayList<>();
items.add("foo");
items.add("bar");
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).save("foo");
verify(this.session).save("bar");
}
@Test
public void testDeleteItemsWithSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
List<String> items = new ArrayList<>();
items.add("foo");
items.add("bar");
writer.setDelete(true);
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).delete("foo");
verify(this.session).delete("bar");
}
}

View File

@@ -0,0 +1,290 @@
/*
* Copyright 2017-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j.builder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.data.Neo4jItemReader;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
/**
* @author Glenn Renfro
*/
public class Neo4jItemReaderBuilderTests {
@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
@Mock
private Iterable<String> result;
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
@Test
public void testFullyQualifiedItemReader() throws Exception {
Neo4jItemReader<String> itemReader = new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.orderByStatement("n.age")
.pageSize(50).name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m").build();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class,
"START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", null))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertEquals("The expected value was not returned by reader.", "foo", itemReader.read());
assertEquals("The expected value was not returned by reader.", "bar", itemReader.read());
assertEquals("The expected value was not returned by reader.", "baz", itemReader.read());
}
@Test
public void testCurrentSize() throws Exception {
Neo4jItemReader<String> itemReader = new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.orderByStatement("n.age")
.pageSize(50).name("bar")
.returnStatement("m")
.currentItemCount(0)
.maxItemCount(1)
.build();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class, "START n=node(*) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", null))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertEquals("The expected value was not returned by reader.", "foo", itemReader.read());
assertNull("The expected value was not should be null.", itemReader.read());
}
@Test
public void testResultsWithMatchAndWhereWithParametersWithSession() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("foo", "bar");
Neo4jItemReader<String> itemReader = new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50)
.name("foo")
.parameterValues(params)
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m")
.build();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class,
"START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", params))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertEquals("The expected value was not returned by reader.", "foo", itemReader.read());
}
@Test
public void testNoSessionFactory() {
try {
new Neo4jItemReaderBuilder<String>()
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50)
.name("bar").build();
fail("IllegalArgumentException should have been thrown");
}
catch (IllegalArgumentException iae) {
assertEquals("IllegalArgumentException message did not match the expected result.",
"sessionFactory is required.", iae.getMessage());
}
}
@Test
public void testZeroPageSize() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(0)
.name("foo")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"),
"pageSize must be greater than zero");
}
@Test
public void testZeroMaxItemCount() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(5)
.maxItemCount(0)
.name("foo")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"),
"maxItemCount must be greater than zero");
}
@Test
public void testCurrentItemCountGreaterThanMaxItemCount() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(5)
.maxItemCount(5)
.currentItemCount(6)
.name("foo")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"),
"maxItemCount must be greater than currentItemCount");
}
@Test
public void testNullName() {
validateExceptionMessage(
new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50),
"A name is required when saveState is set to true");
// tests that name is not required if saveState is set to false.
new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.saveState(false)
.pageSize(50)
.build();
}
@Test
public void testNullTargetType() {
validateExceptionMessage(
new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50)
.name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"),
"targetType is required.");
}
@Test
public void testNullStartStatement() {
validateExceptionMessage(
new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50).name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"),
"startStatement is required.");
}
@Test
public void testNullReturnStatement() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.orderByStatement("n.age")
.pageSize(50).name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)"), "returnStatement is required.");
}
@Test
public void testNullOrderByStatement() {
validateExceptionMessage(
new Neo4jItemReaderBuilder<String>()
.sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.pageSize(50)
.name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"),
"orderByStatement is required.");
}
private void validateExceptionMessage(Neo4jItemReaderBuilder<?> builder, String message) {
try {
builder.build();
fail("IllegalArgumentException should have been thrown");
}
catch (IllegalArgumentException iae) {
assertEquals("IllegalArgumentException message did not match the expected result.", message,
iae.getMessage());
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2017-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.extensions.neo4j.builder;
import java.util.ArrayList;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.data.Neo4jItemWriter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Glenn Renfro
*/
public class Neo4jItemWriterBuilderTests {
@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
@Test
public void testBasicWriter() throws Exception{
Neo4jItemWriter<String> writer = new Neo4jItemWriterBuilder<String>()
.sessionFactory(this.sessionFactory)
.build();
List<String> items = new ArrayList<>();
items.add("foo");
items.add("bar");
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).save("foo");
verify(this.session).save("bar");
verify(this.session, never()).delete("foo");
verify(this.session, never()).delete("bar");
}
@Test
public void testBasicDelete() throws Exception{
Neo4jItemWriter<String> writer = new Neo4jItemWriterBuilder<String>().delete(true).sessionFactory(this.sessionFactory).build();
List<String> items = new ArrayList<>();
items.add("foo");
items.add("bar");
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).delete("foo");
verify(this.session).delete("bar");
verify(this.session, never()).save("foo");
verify(this.session, never()).save("bar");
}
@Test
public void testNoSessionFactory() {
try {
new Neo4jItemWriterBuilder<String>().build();
fail("SessionFactory was not set but exception was not thrown.");
} catch (IllegalArgumentException iae) {
assertEquals("sessionFactory is required.", iae.getMessage());
}
}
}