diff --git a/apache-cayenne/pom.xml b/apache-cayenne/pom.xml
new file mode 100644
index 0000000000..23837d7019
--- /dev/null
+++ b/apache-cayenne/pom.xml
@@ -0,0 +1,59 @@
+
+
+ 4.0.0
+
+ apache-cayenne
+ 0.0.1-SNAPSHOT
+ jar
+
+ apache-cayenne
+ Introduction to Apache Cayenne
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 5.1.31
+ 4.0.M5
+ 4.12
+
+
+
+
+ org.apache.cayenne
+ cayenne-server
+ ${cayenne.version}
+
+
+ mysql
+ mysql-connector-java
+ ${mysql.connector.version}
+ runtime
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+
+
+
+
+ org.apache.cayenne.plugins
+ cayenne-modeler-maven-plugin
+ ${cayenne.version}
+
+
+
+
+
diff --git a/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/Article.java b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/Article.java
new file mode 100644
index 0000000000..303c180887
--- /dev/null
+++ b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/Article.java
@@ -0,0 +1,9 @@
+package com.baeldung.apachecayenne.persistent;
+
+import com.baeldung.apachecayenne.persistent.auto._Article;
+
+public class Article extends _Article {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/Author.java b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/Author.java
new file mode 100644
index 0000000000..5a8df57c6e
--- /dev/null
+++ b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/Author.java
@@ -0,0 +1,9 @@
+package com.baeldung.apachecayenne.persistent;
+
+import com.baeldung.apachecayenne.persistent.auto._Author;
+
+public class Author extends _Author {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/auto/_Article.java b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/auto/_Article.java
new file mode 100644
index 0000000000..f6c179fcfd
--- /dev/null
+++ b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/auto/_Article.java
@@ -0,0 +1,47 @@
+package com.baeldung.apachecayenne.persistent.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+
+import com.baeldung.apachecayenne.persistent.Author;
+
+/**
+ * Class _Article was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Article extends CayenneDataObject {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String ID_PK_COLUMN = "id";
+
+ public static final Property CONTENT = Property.create("content", String.class);
+ public static final Property TITLE = Property.create("title", String.class);
+ public static final Property AUTHOR = Property.create("author", Author.class);
+
+ public void setContent(String content) {
+ writeProperty("content", content);
+ }
+ public String getContent() {
+ return (String)readProperty("content");
+ }
+
+ public void setTitle(String title) {
+ writeProperty("title", title);
+ }
+ public String getTitle() {
+ return (String)readProperty("title");
+ }
+
+ public void setAuthor(Author author) {
+ setToOneTarget("author", author, true);
+ }
+
+ public Author getAuthor() {
+ return (Author)readProperty("author");
+ }
+
+
+}
diff --git a/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/auto/_Author.java b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/auto/_Author.java
new file mode 100644
index 0000000000..3d068423c8
--- /dev/null
+++ b/apache-cayenne/src/main/java/com/baeldung/apachecayenne/persistent/auto/_Author.java
@@ -0,0 +1,52 @@
+package com.baeldung.apachecayenne.persistent.auto;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+
+import com.baeldung.apachecayenne.persistent.Article;
+
+/**
+ * Class _Author was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Author extends CayenneDataObject {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String ID_PK_COLUMN = "id";
+
+ public static final Property FIRSTNAME = Property.create("firstname", String.class);
+ public static final Property LASTNAME = Property.create("lastname", String.class);
+ public static final Property> ARTICLES = Property.create("articles", List.class);
+
+ public void setFirstname(String firstname) {
+ writeProperty("firstname", firstname);
+ }
+ public String getFirstname() {
+ return (String)readProperty("firstname");
+ }
+
+ public void setLastname(String lastname) {
+ writeProperty("lastname", lastname);
+ }
+ public String getLastname() {
+ return (String)readProperty("lastname");
+ }
+
+ public void addToArticles(Article obj) {
+ addToManyTarget("articles", obj, true);
+ }
+ public void removeFromArticles(Article obj) {
+ removeToManyTarget("articles", obj, true);
+ }
+ @SuppressWarnings("unchecked")
+ public List getArticles() {
+ return (List)readProperty("articles");
+ }
+
+
+}
diff --git a/apache-cayenne/src/main/resources/cayenne-project.xml b/apache-cayenne/src/main/resources/cayenne-project.xml
new file mode 100644
index 0000000000..3f3c59e0e9
--- /dev/null
+++ b/apache-cayenne/src/main/resources/cayenne-project.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apache-cayenne/src/main/resources/datamap.map.xml b/apache-cayenne/src/main/resources/datamap.map.xml
new file mode 100644
index 0000000000..dc78ad4348
--- /dev/null
+++ b/apache-cayenne/src/main/resources/datamap.map.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apache-cayenne/src/test/java/com/baeldung/apachecayenne/CayenneOperationTests.java b/apache-cayenne/src/test/java/com/baeldung/apachecayenne/CayenneOperationTests.java
new file mode 100644
index 0000000000..da9667b029
--- /dev/null
+++ b/apache-cayenne/src/test/java/com/baeldung/apachecayenne/CayenneOperationTests.java
@@ -0,0 +1,140 @@
+package com.baeldung.apachecayenne;
+
+import com.baeldung.apachecayenne.persistent.Article;
+import com.baeldung.apachecayenne.persistent.Author;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.query.SQLTemplate;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+
+public class CayenneOperationTests {
+ private static ObjectContext context = null;
+
+ @BeforeClass
+ public static void setupTheCayenneContext(){
+ ServerRuntime cayenneRuntime = ServerRuntime.builder()
+ .addConfig("cayenne-project.xml")
+ .build();
+ context = cayenneRuntime.newContext();
+ }
+
+ @After
+ public void deleteAllRecords(){
+ SQLTemplate deleteArticles = new SQLTemplate(Article.class, "delete from article");
+ SQLTemplate deleteAuthors = new SQLTemplate(Author.class, "delete from author");
+
+ context.performGenericQuery(deleteArticles);
+ context.performGenericQuery(deleteAuthors);
+ }
+
+ @Test
+ public void givenAuthor_whenInsert_thenWeGetOneRecordInTheDatabase(){
+ Author author = context.newObject(Author.class);
+ author.setFirstname("Paul");
+ author.setLastname("Smith");
+
+ context.commitChanges();
+
+ long records = ObjectSelect.dataRowQuery(Author.class).selectCount(context);
+ assertEquals(1, records);
+ }
+
+ @Test
+ public void givenAuthor_whenInsert_andQueryByFirstName_thenWeGetTheAuthor(){
+ Author author = context.newObject(Author.class);
+ author.setFirstname("Paul");
+ author.setLastname("Smith");
+
+ context.commitChanges();
+
+ Author expectedAuthor = ObjectSelect.query(Author.class)
+ .where(Author.FIRSTNAME.eq("Paul"))
+ .selectOne(context);
+
+ assertEquals("Paul", expectedAuthor.getFirstname());
+ assertEquals("Smith", expectedAuthor.getLastname());
+ }
+
+ @Test
+ public void givenTwoAuthor_whenInsert_andQueryAll_thenWeGetTwoAuthors(){
+ Author firstAuthor = context.newObject(Author.class);
+ firstAuthor.setFirstname("Paul");
+ firstAuthor.setLastname("Smith");
+
+ Author secondAuthor = context.newObject(Author.class);
+ secondAuthor.setFirstname("Ludovic");
+ secondAuthor.setLastname("Garcia");
+
+ context.commitChanges();
+
+ List authors = ObjectSelect.query(Author.class).select(context);
+ assertEquals(2, authors.size());
+ }
+
+ @Test
+ public void givenAuthor_whenUpdating_thenWeGetAnUpatedeAuthor(){
+ Author author = context.newObject(Author.class);
+ author.setFirstname("Paul");
+ author.setLastname("Smith");
+ context.commitChanges();
+
+ Author expectedAuthor = ObjectSelect.query(Author.class)
+ .where(Author.FIRSTNAME.eq("Paul"))
+ .selectOne(context);
+ expectedAuthor.setLastname("Smith 2");
+ context.commitChanges();
+
+ assertEquals(author.getFirstname(), expectedAuthor.getFirstname());
+ assertEquals(author.getLastname(), expectedAuthor.getLastname());
+ }
+
+ @Test
+ public void givenAuthor_whenDeleting_thenWeLostHisDetails(){
+ Author author = context.newObject(Author.class);
+ author.setFirstname("Paul");
+ author.setLastname("Smith");
+ context.commitChanges();
+
+ Author savedAuthor = ObjectSelect.query(Author.class)
+ .where(Author.FIRSTNAME.eq("Paul")).selectOne(context);
+ if(savedAuthor != null) {
+ context.deleteObjects(author);
+ context.commitChanges();
+ }
+
+ Author expectedAuthor = ObjectSelect.query(Author.class)
+ .where(Author.FIRSTNAME.eq("Paul")).selectOne(context);
+ assertNull(expectedAuthor);
+ }
+
+ @Test
+ public void givenAuthor_whenAttachingToArticle_thenTheRelationIsMade(){
+ Author author = context.newObject(Author.class);
+ author.setFirstname("Paul");
+ author.setLastname("Smith");
+
+ Article article = context.newObject(Article.class);
+ article.setTitle("My post title");
+ article.setContent("The content");
+ article.setAuthor(author);
+
+ context.commitChanges();
+
+ Author expectedAuthor = ObjectSelect.query(Author.class)
+ .where(Author.LASTNAME.eq("Smith"))
+ .selectOne(context);
+
+ Article expectedArticle = (expectedAuthor.getArticles()).get(0);
+ assertEquals(article.getTitle(), expectedArticle.getTitle());
+ }
+
+}
diff --git a/libraries/pom.xml b/libraries/pom.xml
index 77e7c2634a..da89318e50 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -620,6 +620,7 @@
8.2.0
0.6.5
0.9.0
+ 15.2
2.9.9
1.5.1
2.3.0
diff --git a/pom.xml b/pom.xml
index 015efbab05..dd0871ae79 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,7 @@
+ apache-cayenne
aws
akka-streams
algorithms