diff --git a/.gitignore b/.gitignore
index 7725bf202e..b731787052 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,4 +120,7 @@ libraries-2/src/test/resources/crawler4j/**
devDb*.db
#jaxb
-*.xjb
\ No newline at end of file
+*.xjb
+
+#neo4j
+persistence-modules/neo4j/data/**
\ No newline at end of file
diff --git a/core-java-modules/core-java-8-datetime-2/README.md b/core-java-modules/core-java-8-datetime-2/README.md
index 56f66c10fb..b61937371f 100644
--- a/core-java-modules/core-java-8-datetime-2/README.md
+++ b/core-java-modules/core-java-8-datetime-2/README.md
@@ -3,4 +3,5 @@
- [Generating Random Dates in Java](https://www.baeldung.com/java-random-dates)
- [Creating a LocalDate with Values in Java](https://www.baeldung.com/java-creating-localdate-with-values)
- [Parsing Date Strings with Varying Formats](https://www.baeldung.com/java-parsing-dates-many-formats)
+- [How Many Days Are There in a Particular Month of a Given Year?](https://www.baeldung.com/days-particular-month-given-year)
- [[<-- Prev]](/core-java-modules/core-java-datetime-java8-1)
diff --git a/core-java-modules/core-java-arrays-operations-advanced/README.md b/core-java-modules/core-java-arrays-operations-advanced/README.md
index 1f55338644..e3465c9fa3 100644
--- a/core-java-modules/core-java-arrays-operations-advanced/README.md
+++ b/core-java-modules/core-java-arrays-operations-advanced/README.md
@@ -12,3 +12,4 @@ This module contains articles about advanced operations on arrays in Java. They
- [Concatenate Two Arrays in Java](https://www.baeldung.com/java-concatenate-arrays)
- [Performance of System.arraycopy() vs. Arrays.copyOf()](https://www.baeldung.com/java-system-arraycopy-arrays-copyof-performance)
- [Slicing Arrays in Java](https://www.baeldung.com/java-slicing-arrays)
+- [Combining Two or More Byte Arrays](https://www.baeldung.com/java-concatenate-byte-arrays)
diff --git a/core-java-modules/core-java-datetime-string-2/README.md b/core-java-modules/core-java-datetime-string-2/README.md
index d59f3b3fca..6ff19a44ab 100644
--- a/core-java-modules/core-java-datetime-string-2/README.md
+++ b/core-java-modules/core-java-datetime-string-2/README.md
@@ -4,3 +4,4 @@ This module contains articles about parsing and formatting Java date and time ob
### Relevant Articles:
- [Convert String to Instant](https://www.baeldung.com/java-string-to-instant)
+- [Sort Date Strings in Java](https://www.baeldung.com/java-sort-date-strings)
diff --git a/core-java-modules/core-java-string-operations-6/README.md b/core-java-modules/core-java-string-operations-6/README.md
index 190880a5dd..a6e5380424 100644
--- a/core-java-modules/core-java-string-operations-6/README.md
+++ b/core-java-modules/core-java-string-operations-6/README.md
@@ -2,3 +2,4 @@
### Relevant Articles:
- [Find the Longest Word in a Given String in Java](https://www.baeldung.com/java-longest-word-string)
+- [Check if a String Is All Uppercase or Lowercase in Java](https://www.baeldung.com/java-check-string-uppercase-lowercase)
diff --git a/gradle-modules/gradle-7/README.md b/gradle-modules/gradle-7/README.md
index 6e29e3db30..98fb968e82 100644
--- a/gradle-modules/gradle-7/README.md
+++ b/gradle-modules/gradle-7/README.md
@@ -5,3 +5,4 @@
- [Working With Multiple Repositories in Gradle](https://www.baeldung.com/java-gradle-multiple-repositories)
- [Different Dependency Version Declarations in Gradle](https://www.baeldung.com/gradle-different-dependency-version-declarations)
- [Generating Javadoc With Gradle](https://www.baeldung.com/java-gradle-javadoc)
+- [Generating WSDL Stubs With Gradle](https://www.baeldung.com/java-gradle-create-wsdl-stubs)
diff --git a/image-compressing/README.md b/image-compressing/README.md
index b2556062b8..d90bb53344 100644
--- a/image-compressing/README.md
+++ b/image-compressing/README.md
@@ -1 +1,4 @@
-This module contains tutorials related to the image compression in Java.
\ No newline at end of file
+This module contains tutorials related to the image compression in Java.
+
+## Relevant Articles
+- [Lossy and Lossless Image Compression Using Java](https://www.baeldung.com/java-image-compression-lossy-lossless)
diff --git a/image-processing/pom.xml b/image-processing/pom.xml
index 2a2b92ca48..5c59a53337 100644
--- a/image-processing/pom.xml
+++ b/image-processing/pom.xml
@@ -91,10 +91,15 @@
webcam-capture
${webcam-capture.version}
+
+ org.openjfx
+ javafx-graphics
+ ${javafx-graphics.version}
+
- 1.3.5
+ 1.3.10
1.51h
3.3.2
4.5.1
@@ -105,6 +110,7 @@
4.2
0.4.11
1.5.5
+ 11
\ No newline at end of file
diff --git a/jackson-modules/jackson-annotations/README.md b/jackson-modules/jackson-annotations/README.md
index 8b405ec778..e3cb6e6b7f 100644
--- a/jackson-modules/jackson-annotations/README.md
+++ b/jackson-modules/jackson-annotations/README.md
@@ -9,3 +9,4 @@ This module contains articles about Jackson annotations.
- [Jackson JSON Views](https://www.baeldung.com/jackson-json-view-annotation)
- [Deduction-Based Polymorphism in Jackson 2.12](https://www.baeldung.com/jackson-deduction-based-polymorphism)
- [@JsonIgnore vs @Transient](https://www.baeldung.com/java-jsonignore-vs-transient)
+- [@JsonMerge Annotation in Jackson](https://www.baeldung.com/java-jsonmerge-annotation-jackson)
diff --git a/jmeter/README.md b/jmeter/README.md
index f01fa4eca5..fe6dd2c539 100644
--- a/jmeter/README.md
+++ b/jmeter/README.md
@@ -56,3 +56,4 @@ Enjoy it :)
- [Basic Authentication in JMeter](https://www.baeldung.com/jmeter-basic-auth)
- [JMeter: Latency vs. Load Time](https://www.baeldung.com/java-jmeter-latency-vs-load-time)
- [How Do I Generate a Dashboard Report in JMeter?](https://www.baeldung.com/jmeter-dashboard-report)
+- [Run JMeter .jmx File From the Command Line and Configure the Report File](https://www.baeldung.com/java-jmeter-command-line)
diff --git a/libraries-http-2/README.md b/libraries-http-2/README.md
index 2b94896ab9..f1215a0b11 100644
--- a/libraries-http-2/README.md
+++ b/libraries-http-2/README.md
@@ -12,5 +12,6 @@ This module contains articles about HTTP libraries.
- [Download a Binary File Using OkHttp](https://www.baeldung.com/java-okhttp-download-binary-file)
- [Trusting a Self-Signed Certificate in OkHttp](https://www.baeldung.com/okhttp-self-signed-cert)
- [Trusting All Certificates in OkHttp](https://www.baeldung.com/okhttp-client-trust-all-certificates)
+- [Mock a URL Connection in Java](https://www.baeldung.com/java-simulate-url-connection)
- More articles [[<-- prev]](/libraries-http)
diff --git a/persistence-modules/neo4j/README.md b/persistence-modules/neo4j/README.md
new file mode 100644
index 0000000000..354d9605db
--- /dev/null
+++ b/persistence-modules/neo4j/README.md
@@ -0,0 +1,15 @@
+## Spring Data Neo4j
+
+### Relevant Articles:
+- [A Guide to Neo4J with Java](https://www.baeldung.com/java-neo4j)
+
+### Build the Project with Tests Running
+```
+mvn clean install
+```
+
+### Run Tests Directly
+```
+mvn test
+```
+
diff --git a/persistence-modules/neo4j/pom.xml b/persistence-modules/neo4j/pom.xml
new file mode 100644
index 0000000000..91a488637a
--- /dev/null
+++ b/persistence-modules/neo4j/pom.xml
@@ -0,0 +1,117 @@
+
+
+ 4.0.0
+ neo4j
+ 1.0
+ neo4j
+
+
+ com.baeldung
+ persistence-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.neo4j
+ neo4j
+ ${neo4j.version}
+
+
+ org.neo4j
+ neo4j-ogm-core
+ ${neo4j-ogm-core.version}
+
+
+ org.neo4j
+ neo4j-ogm-embedded-driver
+ ${neo4j-ogm-embedded.version}
+
+
+ org.neo4j
+ neo4j-ogm-bolt-driver
+ ${neo4j-ogm-bolt-driver.version}
+
+
+ org.neo4j.driver
+ neo4j-java-driver
+ ${neo4j-java-driver.version}
+
+
+ org.neo4j
+ neo4j-jdbc-driver
+ ${neo4j-jdbc.version}
+ runtime
+
+
+ com.voodoodyne.jackson.jsog
+ jackson-jsog
+ ${jackson-jsog.version}
+ compile
+
+
+ org.neo4j
+ neo4j-kernel
+ ${neo4j.version}
+ test-jar
+
+
+ org.neo4j.app
+ neo4j-server
+ ${neo4j.version}
+ test-jar
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.neo4j
+ neo4j-ogm-test
+ ${neo4j-ogm.version}
+ test
+
+
+ org.neo4j.test
+ neo4j-harness
+ ${neo4j.version}
+ test
+
+
+ org.testcontainers
+ neo4j
+ ${testcontainers.version}
+ test
+
+
+ org.jetbrains
+ annotations
+
+
+ org.apache.commons
+ commons-compress
+
+
+ javax.xml.bind
+ jaxb-api
+
+
+
+
+
+
+ 5.6.0
+ 5.8.0
+ 1.1.2
+ 3.1.22
+ 4.0.5
+ 3.2.39
+ 4.0.9
+ 4.0.5
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Car.java b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Car.java
new file mode 100644
index 0000000000..d396cc94e7
--- /dev/null
+++ b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Car.java
@@ -0,0 +1,50 @@
+package com.baeldung.neo4j.domain;
+
+import static org.neo4j.ogm.annotation.Relationship.Direction.INCOMING;
+
+import org.neo4j.ogm.annotation.GeneratedValue;
+import org.neo4j.ogm.annotation.Id;
+import org.neo4j.ogm.annotation.NodeEntity;
+import org.neo4j.ogm.annotation.Relationship;
+
+@NodeEntity
+public class Car {
+ @Id @GeneratedValue
+ private Long id;
+
+ private String make;
+
+ @Relationship(direction = INCOMING)
+ private Company company;
+
+ public Car(String make, String model) {
+ this.make = make;
+ this.model = model;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getMake() {
+ return make;
+ }
+
+ public void setMake(String make) {
+ this.make = make;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ private String model;
+}
diff --git a/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Company.java b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Company.java
new file mode 100644
index 0000000000..7749db9d74
--- /dev/null
+++ b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Company.java
@@ -0,0 +1,42 @@
+package com.baeldung.neo4j.domain;
+
+import org.neo4j.ogm.annotation.NodeEntity;
+import org.neo4j.ogm.annotation.Relationship;
+
+@NodeEntity
+public class Company {
+ private Long id;
+
+ private String name;
+
+ @Relationship(type="owns")
+ private Car car;
+
+ public Company(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Car getCar() {
+ return car;
+ }
+
+ public void setCar(Car car) {
+ this.car = car;
+ }
+}
diff --git a/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Movie.java b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Movie.java
new file mode 100644
index 0000000000..c6a0a9a529
--- /dev/null
+++ b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Movie.java
@@ -0,0 +1,66 @@
+package com.baeldung.neo4j.domain;
+
+import static org.neo4j.ogm.annotation.Relationship.Direction.INCOMING;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.voodoodyne.jackson.jsog.JSOGGenerator;
+
+import org.neo4j.ogm.annotation.GeneratedValue;
+import org.neo4j.ogm.annotation.Id;
+import org.neo4j.ogm.annotation.NodeEntity;
+import org.neo4j.ogm.annotation.Relationship;
+
+import java.util.Collection;
+import java.util.List;
+
+@JsonIdentityInfo(generator = JSOGGenerator.class)
+
+@NodeEntity
+public class Movie {
+ @Id @GeneratedValue
+ Long id;
+
+ private String title;
+
+ private int released;
+ private String tagline;
+
+ @Relationship(type = "ACTED_IN", direction = INCOMING)
+ private List roles;
+
+ public Movie() {
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public int getReleased() {
+ return released;
+ }
+
+ public String getTagline() {
+ return tagline;
+ }
+
+ public Collection getRoles() {
+ return roles;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setReleased(int released) {
+ this.released = released;
+ }
+
+ public void setTagline(String tagline) {
+ this.tagline = tagline;
+ }
+
+ public void setRoles(List roles) {
+ this.roles = roles;
+ }
+
+}
diff --git a/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Person.java b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Person.java
new file mode 100644
index 0000000000..a9491ee876
--- /dev/null
+++ b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Person.java
@@ -0,0 +1,52 @@
+package com.baeldung.neo4j.domain;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.voodoodyne.jackson.jsog.JSOGGenerator;
+
+import org.neo4j.ogm.annotation.GeneratedValue;
+import org.neo4j.ogm.annotation.Id;
+import org.neo4j.ogm.annotation.NodeEntity;
+import org.neo4j.ogm.annotation.Relationship;
+
+import java.util.List;
+
+@JsonIdentityInfo(generator = JSOGGenerator.class)
+@NodeEntity
+public class Person {
+ @Id @GeneratedValue
+ Long id;
+
+ private String name;
+ private int born;
+
+ @Relationship(type = "ACTED_IN")
+ private List movies;
+
+ public Person() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getBorn() {
+ return born;
+ }
+
+ public List getMovies() {
+ return movies;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setBorn(int born) {
+ this.born = born;
+ }
+
+ public void setMovies(List movies) {
+ this.movies = movies;
+ }
+
+}
diff --git a/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Role.java b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Role.java
new file mode 100644
index 0000000000..0c91e67f0c
--- /dev/null
+++ b/persistence-modules/neo4j/src/main/java/com/baeldung/neo4j/domain/Role.java
@@ -0,0 +1,50 @@
+package com.baeldung.neo4j.domain;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.voodoodyne.jackson.jsog.JSOGGenerator;
+import org.neo4j.ogm.annotation.EndNode;
+import org.neo4j.ogm.annotation.GeneratedValue;
+import org.neo4j.ogm.annotation.Id;
+import org.neo4j.ogm.annotation.RelationshipEntity;
+import org.neo4j.ogm.annotation.StartNode;
+
+import java.util.Collection;
+
+@JsonIdentityInfo(generator = JSOGGenerator.class)
+@RelationshipEntity(type = "ACTED_IN")
+public class Role {
+ @Id @GeneratedValue
+ Long id;
+ private Collection roles;
+ @StartNode
+ private Person person;
+ @EndNode
+ private Movie movie;
+
+ public Role() {
+ }
+
+ public Collection getRoles() {
+ return roles;
+ }
+
+ public Person getPerson() {
+ return person;
+ }
+
+ public Movie getMovie() {
+ return movie;
+ }
+
+ public void setRoles(Collection roles) {
+ this.roles = roles;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+
+ public void setMovie(Movie movie) {
+ this.movie = movie;
+ }
+}
diff --git a/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4JServerLiveTest.java b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4JServerLiveTest.java
new file mode 100644
index 0000000000..2232c68e8e
--- /dev/null
+++ b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4JServerLiveTest.java
@@ -0,0 +1,79 @@
+package com.baeldung.neo4j;
+
+import static com.baeldung.neo4j.TestContainersTestBase.DEFAULT_PASSWORD;
+import static com.baeldung.neo4j.TestContainersTestBase.getNewBoltConnection;
+import static com.baeldung.neo4j.TestContainersTestBase.neo4jServer;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.neo4j.driver.Driver;
+import org.neo4j.driver.Result;
+import org.neo4j.driver.Session;
+
+
+/**
+ * To run this test you will need to have an instance of the docker running on your machine (Docker desktop - for Windows and Docker instance for linux)
+ * After your docker instance is up run this test
+ */
+class Neo4JServerLiveTest {
+
+ private static Session session;
+ private static Driver driver;
+
+ @BeforeAll
+ public static void setup() {
+ driver = getNewBoltConnection();
+ }
+
+ @AfterAll
+ public static void tearDown() {
+ driver.close();
+ }
+
+ @Test
+ void standAloneDriver() {
+ session = driver.session();
+ session.run("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
+ "-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
+ "RETURN baeldung, tesla");
+
+ Result result = session.run("MATCH (company:Company)-[:owns]-> (car:Car)" +
+ "WHERE car.make='tesla' and car.model='modelX'" +
+ "RETURN company.name");
+
+ assertTrue(result.hasNext());
+ assertEquals("Baeldung", result.next().get("company.name").asString());
+
+ session.close();
+ }
+
+ @Test
+ void standAloneJdbc() throws Exception {
+ String uri = "jdbc:neo4j:" + neo4jServer.getBoltUrl() + "/?user=neo4j,password=" + DEFAULT_PASSWORD + ",scheme=basic";
+ Connection con = DriverManager.getConnection(uri);
+
+ // Querying
+ try (Statement stmt = con.createStatement()) {
+ stmt.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
+ "-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
+ "RETURN baeldung, tesla");
+
+ ResultSet rs = stmt.executeQuery("MATCH (company:Company)-[:owns]-> (car:Car)" +
+ "WHERE car.make='tesla' and car.model='modelX'" +
+ "RETURN company.name");
+
+ while (rs.next()) {
+ assertEquals("Baeldung", rs.getString("company.name"));
+ }
+ }
+ con.close();
+ }
+}
diff --git a/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4jLiveTest.java b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4jLiveTest.java
similarity index 63%
rename from persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4jLiveTest.java
rename to persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4jLiveTest.java
index 1ff01b93a1..8591dd3a8d 100644
--- a/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4jLiveTest.java
+++ b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4jLiveTest.java
@@ -1,7 +1,10 @@
package com.baeldung.neo4j;
+import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME;
+
import java.io.File;
+import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -10,101 +13,111 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.neo4j.configuration.GraphDatabaseSettings;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.api.DatabaseManagementServiceBuilder;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Result;
-import org.neo4j.graphdb.factory.GraphDatabaseFactory;
+import org.neo4j.graphdb.Transaction;
+/**
+ * To run this test you will need to have an instance of the docker running on your machine (Docker desktop - for Windows and Docker instance for linux)
+ * After your docker instance is up run this test
+ */
public class Neo4jLiveTest {
private static GraphDatabaseService graphDb;
+ private static Transaction transaction;
+ private static DatabaseManagementService managementService;
@Before
public void setUp() {
- GraphDatabaseFactory graphDbFactory = new GraphDatabaseFactory();
- graphDb = graphDbFactory.newEmbeddedDatabase(new File("data/cars"));
+ managementService = new DatabaseManagementServiceBuilder(new File("data/cars").toPath())
+ .setConfig( GraphDatabaseSettings.transaction_timeout, Duration.ofSeconds( 60 ) )
+ .setConfig( GraphDatabaseSettings.preallocate_logical_logs, false ).build();
+ graphDb = managementService.database( DEFAULT_DATABASE_NAME );
}
@After
public void tearDown() {
- graphDb.shutdown();
+ managementService.shutdown();
}
@Test
public void testPersonCar() {
- graphDb.beginTx();
- Node car = graphDb.createNode(Label.label("Car"));
+ transaction = graphDb.beginTx();
+ Node car = transaction.createNode(Label.label("Car"));
car.setProperty("make", "tesla");
car.setProperty("model", "model3");
- Node owner = graphDb.createNode(Label.label("Person"));
+ Node owner = transaction.createNode(Label.label("Person"));
owner.setProperty("firstName", "baeldung");
owner.setProperty("lastName", "baeldung");
owner.createRelationshipTo(car, RelationshipType.withName("owner"));
- Result result = graphDb.execute("MATCH (c:Car) <-[owner]- (p:Person) " +
+ Result result = transaction.execute("MATCH (c:Car) <-[owner]- (p:Person) " +
"WHERE c.make = 'tesla'" +
"RETURN p.firstName, p.lastName");
Map firstResult = result.next();
+
Assert.assertEquals("baeldung", firstResult.get("p.firstName"));
}
@Test
public void testCreateNode() {
-
- graphDb.beginTx();
-
- Result result = graphDb.execute("CREATE (baeldung:Company {name:\"Baeldung\"})" +
+ transaction = graphDb.beginTx();
+ Result result = transaction.execute("CREATE (baeldung:Company {name:\"Baeldung\"})" +
"RETURN baeldung");
Map firstResult = result.next();
Node firstNode = (Node) firstResult.get("baeldung");
+
Assert.assertEquals(firstNode.getProperty("name"), "Baeldung");
}
@Test
public void testCreateNodeAndLink() {
- graphDb.beginTx();
-
- Result result = graphDb.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
+ transaction = graphDb.beginTx();
+ Result result = transaction.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
"-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
"RETURN baeldung, tesla");
Map firstResult = result.next();
+
Assert.assertTrue(firstResult.containsKey("baeldung"));
Assert.assertTrue(firstResult.containsKey("tesla"));
}
@Test
public void testFindAndReturn() {
- graphDb.beginTx();
-
- graphDb.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
+ transaction = graphDb.beginTx();
+ transaction.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
"-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
"RETURN baeldung, tesla");
- Result result = graphDb.execute("MATCH (company:Company)-[:owns]-> (car:Car)" +
+ Result result = transaction.execute("MATCH (company:Company)-[:owns]-> (car:Car)" +
"WHERE car.make='tesla' and car.model='modelX'" +
"RETURN company.name");
Map firstResult = result.next();
+
Assert.assertEquals(firstResult.get("company.name"), "Baeldung");
}
@Test
public void testUpdate() {
- graphDb.beginTx();
-
- graphDb.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
+ transaction = graphDb.beginTx();
+ transaction.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
"-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
"RETURN baeldung, tesla");
- Result result = graphDb.execute("MATCH (car:Car)" +
+ Result result = transaction.execute("MATCH (car:Car)" +
"WHERE car.make='tesla'" +
" SET car.milage=120" +
" SET car :Car:Electro" +
@@ -127,17 +140,16 @@ public class Neo4jLiveTest {
@Test
public void testDelete() {
- graphDb.beginTx();
-
- graphDb.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
+ transaction = graphDb.beginTx();
+ transaction.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
"-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
"RETURN baeldung, tesla");
- graphDb.execute("MATCH (company:Company)" +
+ transaction.execute("MATCH (company:Company)" +
" WHERE company.name='Baeldung'" +
" DELETE company");
- Result result = graphDb.execute("MATCH (company:Company)" +
+ Result result = transaction.execute("MATCH (company:Company)" +
" WHERE company.name='Baeldung'" +
" RETURN company");
@@ -146,14 +158,13 @@ public class Neo4jLiveTest {
@Test
public void testBindings() {
- graphDb.beginTx();
-
+ transaction = graphDb.beginTx();
Map params = new HashMap<>();
params.put("name", "baeldung");
params.put("make", "tesla");
params.put("model", "modelS");
- Result result = graphDb.execute("CREATE (baeldung:Company {name:$name}) " +
+ Result result = transaction.execute("CREATE (baeldung:Company {name:$name}) " +
"-[:owns]-> (tesla:Car {make: $make, model: $model})" +
"RETURN baeldung, tesla", params);
diff --git a/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4jOgmLiveTest.java b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4jOgmLiveTest.java
new file mode 100644
index 0000000000..aa9dd84ed6
--- /dev/null
+++ b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/Neo4jOgmLiveTest.java
@@ -0,0 +1,58 @@
+package com.baeldung.neo4j;
+
+import static com.baeldung.neo4j.TestContainersTestBase.getDriver;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.neo4j.ogm.model.Result;
+import org.neo4j.ogm.session.Session;
+import org.neo4j.ogm.session.SessionFactory;
+
+import com.baeldung.neo4j.domain.Car;
+import com.baeldung.neo4j.domain.Company;
+
+/**
+ * To run this test you will need to have an instance of the docker running on your machine (Docker desktop - for Windows and Docker instance for linux)
+ * After your docker instance is up run this test
+ */
+public class Neo4jOgmLiveTest {
+
+ private static SessionFactory sessionFactory;
+ private static Session session;
+
+ @BeforeAll
+ public static void oneTimeSetUp() {
+ sessionFactory = new SessionFactory(getDriver(), "com.baeldung.neo4j.domain");
+ session = sessionFactory.openSession();
+ session.purgeDatabase();
+ }
+
+ @Test
+ void testOgm() {
+ Car tesla = new Car("tesla", "modelS");
+ Company baeldung = new Company("baeldung");
+
+ baeldung.setCar(tesla);
+
+ session.save(baeldung);
+
+ assertEquals(1, session.countEntitiesOfType(Company.class));
+
+ Map params = new HashMap<>();
+ params.put("make", "tesla");
+ Result result = session.query("MATCH (car:Car) <-[:owns]- (company:Company)" +
+ " WHERE car.make=$make" +
+ " RETURN company", params);
+
+ Map firstResult = result.iterator().next();
+
+ assertEquals(1, firstResult.size());
+
+ Company actual = (Company) firstResult.get("company");
+ assertEquals(actual.getName(), baeldung.getName());
+ }
+}
diff --git a/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/TestContainersTestBase.java b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/TestContainersTestBase.java
new file mode 100644
index 0000000000..eed2237454
--- /dev/null
+++ b/persistence-modules/neo4j/src/test/java/com/baeldung/neo4j/TestContainersTestBase.java
@@ -0,0 +1,132 @@
+package com.baeldung.neo4j;
+
+import static org.neo4j.ogm.drivers.bolt.driver.BoltDriver.CONFIG_PARAMETER_BOLT_LOGGING;
+
+import java.util.Locale;
+import java.util.Optional;
+
+import org.neo4j.driver.AccessMode;
+import org.neo4j.driver.AuthTokens;
+import org.neo4j.driver.GraphDatabase;
+import org.neo4j.driver.Logging;
+import org.neo4j.driver.Session;
+import org.neo4j.driver.SessionConfig;
+import org.neo4j.ogm.config.Configuration;
+import org.neo4j.ogm.driver.Driver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.Neo4jContainer;
+import org.testcontainers.utility.TestcontainersConfiguration;
+
+/**
+ * Reference: https://github.com/neo4j/neo4j-ogm/blob/master/neo4j-ogm-tests/neo4j-ogm-integration-tests/src/test/java/org/neo4j/ogm/testutil/TestContainersTestBase.java
+ */
+public class TestContainersTestBase {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger(TestContainersTestBase.class);
+
+ public static final Driver driver;
+
+ public static final String version;
+
+ public static final String DEFAULT_IMAGE = "neo4j:5";
+
+ public static final String SYS_PROPERTY_ACCEPT_AND_USE_COMMERCIAL_EDITION = "NEO4J_OGM_NEO4J_ACCEPT_AND_USE_COMMERCIAL_EDITION";
+
+ public static final String SYS_PROPERTY_IMAGE_NAME = "NEO4J_OGM_NEO4J_IMAGE_NAME";
+
+ public static final String SYS_PROPERTY_NEO4J_URL = "NEO4J_OGM_NEO4J_URL";
+
+ public static final String SYS_PROPERTY_NEO4J_PASSWORD = "NEO4J_OGM_NEO4J_PASSWORD";
+
+ public static final String DEFAULT_PASSWORD = "12345";
+
+ public static Neo4jContainer neo4jServer;
+
+ public static Configuration.Builder baseConfigurationBuilder;
+
+ public static final String NEO4J_URL = Optional.ofNullable(System.getenv(SYS_PROPERTY_NEO4J_URL)).orElse("");
+
+ public static final String NEO4J_PASSWORD = Optional.ofNullable(System.getenv(SYS_PROPERTY_NEO4J_PASSWORD)).orElse("").trim();
+
+ static {
+
+ boolean acceptAndUseCommercialEdition = hasAcceptedAndWantsToUseCommercialEdition();
+
+ if (!(NEO4J_URL.isEmpty() || NEO4J_PASSWORD.isEmpty())) {
+ LOGGER.info("Using Neo4j instance at {}.", NEO4J_URL);
+ driver = new org.neo4j.ogm.drivers.bolt.driver.BoltDriver();
+ baseConfigurationBuilder = new Configuration.Builder()
+ .uri(NEO4J_URL)
+ .verifyConnection(true)
+ .withCustomProperty(CONFIG_PARAMETER_BOLT_LOGGING, Logging.slf4j())
+ .credentials("neo4j", NEO4J_PASSWORD);
+ driver.configure(baseConfigurationBuilder.build());
+ version = extractVersionFromBolt();
+ } else {
+ LOGGER.info("Using Neo4j test container.");
+ String imageName = Optional.ofNullable(System.getenv(SYS_PROPERTY_IMAGE_NAME))
+ .orElse(DEFAULT_IMAGE + (acceptAndUseCommercialEdition ? "-enterprise" : ""));
+
+ version = extractVersionFromDockerImage(imageName);
+
+ boolean containerReuseSupported = TestcontainersConfiguration
+ .getInstance().environmentSupportsReuse();
+ neo4jServer = new Neo4jContainer<>(imageName)
+ .withReuse(containerReuseSupported);
+
+ if (acceptAndUseCommercialEdition) {
+ neo4jServer.withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes");
+ }
+ neo4jServer.withAdminPassword(DEFAULT_PASSWORD).start();
+
+ driver = new org.neo4j.ogm.drivers.bolt.driver.BoltDriver();
+
+ baseConfigurationBuilder = new Configuration.Builder()
+ .uri(neo4jServer.getBoltUrl())
+ .credentials("neo4j", DEFAULT_PASSWORD)
+ .verifyConnection(true)
+ .withCustomProperty(CONFIG_PARAMETER_BOLT_LOGGING, Logging.slf4j());
+
+ driver.configure(baseConfigurationBuilder.build());
+
+ Runtime.getRuntime().addShutdownHook(new Thread(neo4jServer::stop));
+ }
+ }
+
+ public static org.neo4j.driver.Driver getNewBoltConnection() {
+
+ if (neo4jServer != null) {
+ return GraphDatabase.driver(neo4jServer.getBoltUrl(), AuthTokens.basic("neo4j", DEFAULT_PASSWORD));
+ } else {
+ return GraphDatabase.driver(NEO4J_URL, AuthTokens.basic("neo4j", NEO4J_PASSWORD));
+ }
+ }
+
+ public static boolean hasAcceptedAndWantsToUseCommercialEdition() {
+ return Optional.ofNullable(
+ System.getenv(TestContainersTestBase.SYS_PROPERTY_ACCEPT_AND_USE_COMMERCIAL_EDITION))
+ .orElse("no").toLowerCase(Locale.ENGLISH).equals("yes");
+ }
+
+ public static Driver getDriver() {
+ return driver;
+ }
+
+ private static String extractVersionFromDockerImage(String imageName) {
+ return imageName.replace("neo4j:", "").replace("neo4j/neo4j-experimental:", "").replace("-enterprise", "");
+ }
+
+ private static String extractVersionFromBolt() {
+
+ org.neo4j.driver.Driver driver = getDriver().unwrap(org.neo4j.driver.Driver.class);
+
+ String version;
+ SessionConfig sessionConfig = SessionConfig.builder().withDefaultAccessMode(AccessMode.READ).build();
+ try (Session session = driver.session(sessionConfig)) {
+ version = (String) session.run("CALL dbms.components() YIELD versions").single().get("versions").asList().get(0);
+ }
+ return version.toLowerCase(Locale.ENGLISH);
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml
index a0246e0dfd..fe981d11f3 100644
--- a/persistence-modules/pom.xml
+++ b/persistence-modules/pom.xml
@@ -110,6 +110,7 @@
spring-data-rest
java-mongodb
questdb
+ neo4j
diff --git a/persistence-modules/spring-data-elasticsearch/README.md b/persistence-modules/spring-data-elasticsearch/README.md
index 22126c2f00..dfa581a59a 100644
--- a/persistence-modules/spring-data-elasticsearch/README.md
+++ b/persistence-modules/spring-data-elasticsearch/README.md
@@ -6,6 +6,7 @@
- [Guide to Elasticsearch in Java](https://www.baeldung.com/elasticsearch-java)
- [Geospatial Support in ElasticSearch](https://www.baeldung.com/elasticsearch-geo-spatial)
- [A Simple Tagging Implementation with Elasticsearch](https://www.baeldung.com/elasticsearch-tagging)
+- [What Is Elasticsearch?](https://www.baeldung.com/java-elasticsearch)
### Build the Project with Tests Running
```
diff --git a/persistence-modules/spring-data-neo4j/pom.xml b/persistence-modules/spring-data-neo4j/pom.xml
index 8c5030779b..33f2966db3 100644
--- a/persistence-modules/spring-data-neo4j/pom.xml
+++ b/persistence-modules/spring-data-neo4j/pom.xml
@@ -14,26 +14,11 @@
-
- org.neo4j
- neo4j
- ${neo4j.version}
-
-
- org.neo4j
- neo4j-ogm-core
- ${neo4j-ogm.version}
-
org.neo4j
neo4j-ogm-embedded-driver
${neo4j-ogm.version}
-
- org.neo4j.driver
- neo4j-java-driver
- ${neo4j-java-driver.version}
-
org.springframework.data
spring-data-neo4j
@@ -51,24 +36,6 @@
${spring-boot.version}
test
-
- org.neo4j
- neo4j-kernel
- ${neo4j.version}
- test-jar
-
-
- org.neo4j.app
- neo4j-server
- ${neo4j.version}
- test-jar
-
-
- commons-logging
- commons-logging
-
-
-
org.neo4j
neo4j-ogm-test
@@ -89,7 +56,6 @@
- 1.6.2
3.4.6
5.0.1.RELEASE
1.1
diff --git a/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4JServerLiveTest.java b/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4JServerLiveTest.java
deleted file mode 100644
index bf187ccb2d..0000000000
--- a/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4JServerLiveTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.baeldung.neo4j;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.ResultSet;
-import java.sql.Statement;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.neo4j.driver.v1.AuthTokens;
-import org.neo4j.driver.v1.Driver;
-import org.neo4j.driver.v1.GraphDatabase;
-import org.neo4j.driver.v1.Session;
-import org.neo4j.driver.v1.StatementResult;
-
-@Ignore
-public class Neo4JServerLiveTest {
-
- @Test
- public void standAloneDriver() {
- Driver driver = GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "12345"));
- Session session = driver.session();
-
- session.run("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
- "-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
- "RETURN baeldung, tesla");
-
- StatementResult result = session.run("MATCH (company:Company)-[:owns]-> (car:Car)" +
- "WHERE car.make='tesla' and car.model='modelX'" +
- "RETURN company.name");
-
- Assert.assertTrue(result.hasNext());
- Assert.assertEquals(result.next().get("company.name").asString(), "Baeldung");
-
- session.close();
- driver.close();
- }
-
- @Test
- public void standAloneJdbc() throws Exception {
- Connection con = DriverManager.getConnection("jdbc:neo4j:bolt://localhost/?user=neo4j,password=12345,scheme=basic");
-
- // Querying
- try (Statement stmt = con.createStatement()) {
- stmt.execute("CREATE (baeldung:Company {name:\"Baeldung\"}) " +
- "-[:owns]-> (tesla:Car {make: 'tesla', model: 'modelX'})" +
- "RETURN baeldung, tesla");
-
- ResultSet rs = stmt.executeQuery("MATCH (company:Company)-[:owns]-> (car:Car)" +
- "WHERE car.make='tesla' and car.model='modelX'" +
- "RETURN company.name");
-
- while (rs.next()) {
- Assert.assertEquals(rs.getString("company.name"), "Baeldung");
- }
- }
- con.close();
- }
-}
diff --git a/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4jOgmLiveTest.java b/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4jOgmLiveTest.java
deleted file mode 100644
index 96e5e76402..0000000000
--- a/persistence-modules/spring-data-neo4j/src/test/java/com/baeldung/neo4j/Neo4jOgmLiveTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.baeldung.neo4j;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.neo4j.ogm.config.Configuration;
-import org.neo4j.ogm.model.Result;
-import org.neo4j.ogm.session.Session;
-import org.neo4j.ogm.session.SessionFactory;
-
-import com.baeldung.spring.data.neo4j.domain.Car;
-import com.baeldung.spring.data.neo4j.domain.Company;
-
-public class Neo4jOgmLiveTest {
-
- @Test
- public void testOgm() {
- Configuration conf = new Configuration.Builder().build();
-
- SessionFactory factory = new SessionFactory(conf, "com.baeldung.spring.data.neo4j.domain");
- Session session = factory.openSession();
-
- Car tesla = new Car("tesla", "modelS");
- Company baeldung = new Company("baeldung");
-
- baeldung.setCar(tesla);
-
- session.save(baeldung);
-
- Assert.assertEquals(1, session.countEntitiesOfType(Company.class));
-
- Map params = new HashMap<>();
- params.put("make", "tesla");
- Result result = session.query("MATCH (car:Car) <-[:owns]- (company:Company)" +
- " WHERE car.make=$make" +
- " RETURN company", params);
-
- Map firstResult = result.iterator().next();
-
- Assert.assertEquals(firstResult.size(), 1);
-
- Company actual = (Company) firstResult.get("company");
- Assert.assertEquals(actual.getName(), baeldung.getName());
- }
-}
diff --git a/pom.xml b/pom.xml
index b6552706b5..b8bc85d634 100644
--- a/pom.xml
+++ b/pom.xml
@@ -369,7 +369,7 @@
persistence-modules/hibernate-ogm
persistence-modules/java-cassandra
persistence-modules/spring-data-cassandra-reactive
- persistence-modules/spring-data-neo4j
+ persistence-modules/spring-data-neo4j
@@ -474,8 +474,6 @@
apache-spark
- image-processing
-
jenkins-modules
jhipster-modules
@@ -548,7 +546,7 @@
persistence-modules/hibernate-ogm
persistence-modules/java-cassandra
persistence-modules/spring-data-cassandra-reactive
- persistence-modules/spring-data-neo4j
+ persistence-modules/spring-data-neo4j
@@ -637,8 +635,6 @@
apache-spark
- image-processing
-
jenkins-modules
jhipster-modules
@@ -938,6 +934,7 @@
web-modules
xml
xml-2
+ image-processing
@@ -1204,6 +1201,7 @@
web-modules
xml
xml-2
+ image-processing
diff --git a/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/config/RedisConfiguration.java b/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/config/RedisConfiguration.java
index 2c050af8af..896339646b 100644
--- a/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/config/RedisConfiguration.java
+++ b/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/config/RedisConfiguration.java
@@ -2,7 +2,6 @@ package com.baelding.springbootredis.config;
import com.baelding.springbootredis.model.Session;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
diff --git a/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/controller/SessionController.java b/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/controller/SessionController.java
index 156ca0195f..020de31019 100644
--- a/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/controller/SessionController.java
+++ b/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/controller/SessionController.java
@@ -6,7 +6,6 @@ import com.baelding.springbootredis.service.cache.session.SessionCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
diff --git a/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/model/Session.java b/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/model/Session.java
index 5c9d26147e..b9c8668104 100644
--- a/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/model/Session.java
+++ b/spring-boot-modules/spring-boot-redis/src/main/java/com/baelding/springbootredis/model/Session.java
@@ -1,19 +1,25 @@
package com.baelding.springbootredis.model;
import lombok.Builder;
-import lombok.Data;
import lombok.EqualsAndHashCode;
+import lombok.Data;
+import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
-@Data
@Builder
@EqualsAndHashCode
@RedisHash(timeToLive = 60L)
+@Data
+@NoArgsConstructor
public class Session {
@Id
private String id;
@TimeToLive
private Long expirationInSeconds;
+ public Session(String id, Long expirationInSeconds) {
+ this.id = id;
+ this.expirationInSeconds = expirationInSeconds;
+ }
}
diff --git a/spring-boot-modules/spring-boot-redis/src/test/java/com/baelding/springbootredis/SpringBootRedisApplicationIntegrationTest.java b/spring-boot-modules/spring-boot-redis/src/test/java/com/baelding/springbootredis/SpringBootRedisApplicationIntegrationTest.java
index 69c88706e4..d1ee5ad68a 100644
--- a/spring-boot-modules/spring-boot-redis/src/test/java/com/baelding/springbootredis/SpringBootRedisApplicationIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-redis/src/test/java/com/baelding/springbootredis/SpringBootRedisApplicationIntegrationTest.java
@@ -8,6 +8,7 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.web.reactive.server.WebTestClient;
@@ -19,6 +20,7 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RedisTestConfiguration.class)
+@AutoConfigureMockMvc
class SpringBootRedisApplicationIntegrationTest {
private static final String V1_SESSIONS_ENDPOINT = "/v1/sessions";
diff --git a/spring-reactive-modules/spring-reactive-exceptions/pom.xml b/spring-reactive-modules/spring-reactive-exceptions/pom.xml
index 0dca81c529..a9fdb675f5 100644
--- a/spring-reactive-modules/spring-reactive-exceptions/pom.xml
+++ b/spring-reactive-modules/spring-reactive-exceptions/pom.xml
@@ -20,10 +20,6 @@
org.springframework.boot
spring-boot-starter-webflux
-
- org.springframework.boot
- spring-boot-starter-web
-
org.projectlombok
lombok
diff --git a/spring-web-modules/spring-mvc-java-3/README.md b/spring-web-modules/spring-mvc-java-3/README.md
index 7d843af9ea..f61e004bb5 100644
--- a/spring-web-modules/spring-mvc-java-3/README.md
+++ b/spring-web-modules/spring-mvc-java-3/README.md
@@ -1 +1,2 @@
### Relevant Articles:
+- [Java IllegalStateException: “getInputStream() has already been called for this request”](https://www.baeldung.com/java-servletrequest-illegalstateexception)
diff --git a/spring-web-modules/spring-thymeleaf/README.md b/spring-web-modules/spring-thymeleaf/README.md
index 653e6df5b9..2d1bc848d8 100644
--- a/spring-web-modules/spring-thymeleaf/README.md
+++ b/spring-web-modules/spring-thymeleaf/README.md
@@ -10,6 +10,7 @@ This module contains articles about Spring with Thymeleaf
- [How to Work with Dates in Thymeleaf](https://www.baeldung.com/dates-in-thymeleaf)
- [Working with Fragments in Thymeleaf](https://www.baeldung.com/spring-thymeleaf-fragments)
- [JavaScript Function Call with Thymeleaf](https://www.baeldung.com/thymeleaf-js-function-call)
+- [Add a Checked Attribute to Input Conditionally in Thymeleaf](https://www.baeldung.com/thymeleaf-conditional-checked-attribute)
- [[next -->]](/spring-thymeleaf-2)
### Build the Project
diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md
index 3bb2b1543e..73b88a9885 100644
--- a/testing-modules/mockito-2/README.md
+++ b/testing-modules/mockito-2/README.md
@@ -4,3 +4,4 @@ This module contains articles about Mockito
### Relevant Articles:
- [Mocking a Singleton With Mockito](https://www.baeldung.com/java-mockito-singleton)
+- [Resolving Mockito Exception: Wanted But Not Invoked](https://www.baeldung.com/mockito-exception-wanted-but-not-invoked)
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/nullmatcher/Helper.java b/testing-modules/mockito-2/src/main/java/com/baeldung/nullmatcher/Helper.java
new file mode 100644
index 0000000000..d15196faa5
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/nullmatcher/Helper.java
@@ -0,0 +1,9 @@
+package com.baeldung.nullmatcher;
+
+class Helper {
+
+ String concat(String a, String b) {
+ return a + b;
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/nullmatcher/Main.java b/testing-modules/mockito-2/src/main/java/com/baeldung/nullmatcher/Main.java
new file mode 100644
index 0000000000..e3c73f9d8a
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/nullmatcher/Main.java
@@ -0,0 +1,11 @@
+package com.baeldung.nullmatcher;
+
+class Main {
+
+ Helper helper = new Helper();
+
+ String methodUnderTest() {
+ return helper.concat("Baeldung", null);
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/nullmatcher/MainUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/nullmatcher/MainUnitTest.java
new file mode 100644
index 0000000000..a0245977e0
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/nullmatcher/MainUnitTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.nullmatcher;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+class MainUnitTest {
+
+ @Mock
+ Helper helper;
+
+ @InjectMocks
+ Main main;
+
+ @BeforeEach
+ void openMocks() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void whenMethodUnderTest_thenSecondParameterNull() {
+ main.methodUnderTest();
+ Mockito.verify(helper)
+ .concat("Baeldung", null);
+ }
+
+ @Test
+ void whenMethodUnderTest_thenSecondParameterNullWithMatchers() {
+ main.methodUnderTest();
+ Mockito.verify(helper)
+ .concat(any(), isNull());
+ }
+
+}
diff --git a/testing-modules/mockito-simple/src/test/java/com/baeldung/mockito/mockfinal/MockFinalsUnitTest.java b/testing-modules/mockito-simple/src/test/java/com/baeldung/mockito/mockfinal/MockFinalsUnitTest.java
index 6041ee1a9b..705f2ac7ad 100644
--- a/testing-modules/mockito-simple/src/test/java/com/baeldung/mockito/mockfinal/MockFinalsUnitTest.java
+++ b/testing-modules/mockito-simple/src/test/java/com/baeldung/mockito/mockfinal/MockFinalsUnitTest.java
@@ -3,10 +3,8 @@ package com.baeldung.mockito.mockfinal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.withSettings;
import org.junit.jupiter.api.Test;
-import org.mockito.MockMakers;
import com.baeldung.mockito.FinalList;
import com.baeldung.mockito.MyList;
@@ -30,14 +28,4 @@ class MockFinalsUnitTest {
assertThat(mock.size()).isNotEqualTo(1);
}
-
- @Test
- public void whenMockFinalMethodMockWorks_withInlineMockMaker() {
- MyList myList = new MyList();
-
- MyList mock = mock(MyList.class, withSettings().mockMaker(MockMakers.INLINE));
- when(mock.finalMethod()).thenReturn(1);
-
- assertThat(mock.finalMethod()).isNotEqualTo(myList.finalMethod());
- }
}
diff --git a/testing-modules/mockito-simple/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/testing-modules/mockito-simple/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
deleted file mode 100644
index ca6ee9cea8..0000000000
--- a/testing-modules/mockito-simple/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
+++ /dev/null
@@ -1 +0,0 @@
-mock-maker-inline
\ No newline at end of file
diff --git a/testing-modules/testing-assertions/pom.xml b/testing-modules/testing-assertions/pom.xml
index 09f4291b78..1da53bd77e 100644
--- a/testing-modules/testing-assertions/pom.xml
+++ b/testing-modules/testing-assertions/pom.xml
@@ -24,6 +24,22 @@
${commons-collections4.version}
test
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+ test
+
+
+ org.hamcrest
+ hamcrest
+ ${hamcrest.version}
+ test
+
+
+ 2.2
+
+
\ No newline at end of file
diff --git a/testing-modules/testing-assertions/src/main/java/com/baeldung/assertequals/Address.java b/testing-modules/testing-assertions/src/main/java/com/baeldung/assertequals/Address.java
new file mode 100644
index 0000000000..905dd44af9
--- /dev/null
+++ b/testing-modules/testing-assertions/src/main/java/com/baeldung/assertequals/Address.java
@@ -0,0 +1,61 @@
+package com.baeldung.assertequals;
+
+public class Address {
+
+ private Long id;
+
+ private String city;
+
+ private String street;
+
+ private String country;
+
+ public Address(final Long id, final String city, final String street, final String country) {
+ this.id = id;
+ this.city = city;
+ this.street = street;
+ this.country = country;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(final Long id) {
+ this.id = id;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(final String city) {
+ this.city = city;
+ }
+
+ public String getStreet() {
+ return street;
+ }
+
+ public void setStreet(final String street) {
+ this.street = street;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(final String country) {
+ this.country = country;
+ }
+
+ @Override
+ public String toString() {
+ return "Address{" +
+ "id=" + id +
+ ", city='" + city + '\'' +
+ ", street='" + street + '\'' +
+ ", country='" + country + '\'' +
+ '}';
+ }
+}
diff --git a/testing-modules/testing-assertions/src/main/java/com/baeldung/assertequals/Person.java b/testing-modules/testing-assertions/src/main/java/com/baeldung/assertequals/Person.java
new file mode 100644
index 0000000000..8ea64357ec
--- /dev/null
+++ b/testing-modules/testing-assertions/src/main/java/com/baeldung/assertequals/Person.java
@@ -0,0 +1,60 @@
+package com.baeldung.assertequals;
+
+public class Person {
+
+ private Long id;
+
+ private String firstName;
+
+ private String lastName;
+
+ private Address address;
+
+ public Person(final Long id, final String firstName, final String lastName) {
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(final Address address) {
+ this.address = address;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(final Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(final String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(final String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return "Person{" +
+ "id=" + id +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", address=" + address +
+ '}';
+ }
+}
diff --git a/testing-modules/testing-assertions/src/test/java/com/baeldung/assertequals/EqualityUnitTest.java b/testing-modules/testing-assertions/src/test/java/com/baeldung/assertequals/EqualityUnitTest.java
new file mode 100644
index 0000000000..bf20e02a1d
--- /dev/null
+++ b/testing-modules/testing-assertions/src/test/java/com/baeldung/assertequals/EqualityUnitTest.java
@@ -0,0 +1,96 @@
+package com.baeldung.assertequals;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Test;
+import org.mockito.internal.matchers.apachecommons.ReflectionEquals;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.samePropertyValuesAs;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class EqualityUnitTest {
+
+ @Test
+ void givenPersons_whenUseRecursiveComparison_thenOk() {
+ Person expected = new Person(1L, "Jane", "Doe");
+ Address address1 = new Address(1L, "New York", "Sesame Street", "United States");
+ expected.setAddress(address1);
+
+ Person actual = new Person(1L, "Jane", "Doe");
+ Address address2 = new Address(1L, "New York", "Sesame Street", "United States");
+ actual.setAddress(address2);
+
+ assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
+ }
+
+ @Test
+ void givenPersons_whenUseRecursiveComparisonIgnoringField_thenOk() {
+ Person expected = new Person(1L, "Jane", "Doe");
+ Person actual = new Person(2L, "Jane", "Doe");
+
+ assertThat(actual)
+ .usingRecursiveComparison()
+ .ignoringFields("id")
+ .isEqualTo(expected);
+ }
+
+ @Test
+ void givenPersons_whenCheckForSamePropertyValues_thenReturnOk() {
+ Person expected = new Person(1L, "Jane", "Doe");
+ Address address1 = new Address(1L, "New York", "Sesame Street", "United States");
+ expected.setAddress(address1);
+
+ Person actual = new Person(1L, "Jane", "Doe");
+ Address address2 = new Address(1L, "New York", "Sesame Street", "United States");
+ actual.setAddress(address2);
+
+ MatcherAssert.assertThat(actual, samePropertyValuesAs(expected, "address"));
+ MatcherAssert.assertThat(actual.getAddress(), samePropertyValuesAs(expected.getAddress()));
+ }
+
+ @Test
+ void givenPerson_whenReflectionToStringBuilder_thenReturnOk() {
+ Person expected = new Person(1L, "Jane", "Doe");
+ Address address1 = new Address(1L, "New York", "Sesame Street", "United States");
+ expected.setAddress(address1);
+
+ Person actual = new Person(1L, "Jane", "Doe");
+ Address address2 = new Address(1L, "New York", "Sesame Street", "United States");
+ actual.setAddress(address2);
+
+ assertThat(ReflectionToStringBuilder.toString(actual, ToStringStyle.SHORT_PREFIX_STYLE))
+ .isEqualTo(ReflectionToStringBuilder.toString(expected, ToStringStyle.SHORT_PREFIX_STYLE));
+ }
+
+ @Test
+ void givenPersons_whenEqualsBuilder_thenReturnTrue() {
+ Person expected = new Person(1L, "Jane", "Doe");
+ Address address1 = new Address(1L, "New York", "Sesame Street", "United States");
+ expected.setAddress(address1);
+
+ Person actual = new Person(1L, "Jane", "Doe");
+ Address address2 = new Address(1L, "New York", "Sesame Street", "United States");
+ actual.setAddress(address2);
+
+ assertTrue(EqualsBuilder.reflectionEquals(expected, actual, "address"));
+ assertTrue(EqualsBuilder.reflectionEquals(expected.getAddress(), actual.getAddress()));
+ }
+
+ @Test
+ void givenPersons_whenReflectionEquals_thenReturnOk() {
+ Person expected = new Person(1L, "Jane", "Doe");
+ Address address1 = new Address(1L, "New York", "Sesame Street", "United States");
+ expected.setAddress(address1);
+
+ Person actual = new Person(1L, "Jane", "Doe");
+ Address address2 = new Address(1L, "New York", "Sesame Street", "United States");
+ actual.setAddress(address2);
+
+ assertTrue(new ReflectionEquals(expected, "address").matches(actual));
+ assertTrue(new ReflectionEquals(expected.getAddress()).matches(actual.getAddress()));
+ }
+
+}