diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/README.MD b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/README.MD
new file mode 100644
index 00000000..452fff89
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/README.MD
@@ -0,0 +1,25 @@
+#
Photo comment DAO exercise :muscle:
+Improve your *Hibernate ORM* and *JPA One-To-Many* relationship mapping skills
+### Task
+Each `Photo` has a list of `PhotoComments`. Comments cannot exits without photo associated. You job is to provide a mapping
+for this **bidirectional *one to many* relationship** and **implement required DAO methods.** Please follow the instructions
+in the todo sections.
+
+To verify your mapping, run `PhotoCommentMappingTest.java`
+To verify your DAO implementation, run `PhotoDaoTest.java`
+
+
+### Pre-conditions :heavy_exclamation_mark:
+You're supposed to be familiar with *JPA* mapping strategies and *Hibernate ORM*
+
+### How to start :question:
+* Just clone the repository and start implementing the **todo** section, verify your changes by running tests
+* If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
+* Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
+
+### Related materials :information_source:
+ * [JPA and Hibernate basics tutorial](https://github.com/boy4uck/jpa-hibernate-tutorial/tree/master/jpa-hibernate-basics)
+ * [@OneToMany association](http://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#associations-one-to-many)
+ * [The best way to map a @OneToMany relationship with JPA and Hibernate](https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/)
+
+
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/pom.xml b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/pom.xml
new file mode 100644
index 00000000..a98e516a
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ 3-0-jpa-and-hibernate
+ com.bobocode
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ 3-2-2-photo-comment-dao
+
+
+
+ com.bobocode
+ jpa-hibernate-util
+ 1.0-SNAPSHOT
+
+
+
+
\ No newline at end of file
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/dao/PhotoDao.java b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/dao/PhotoDao.java
new file mode 100644
index 00000000..43e4c01b
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/dao/PhotoDao.java
@@ -0,0 +1,49 @@
+package com.bobocode.dao;
+
+import com.bobocode.model.Photo;
+
+import java.util.List;
+
+/**
+ * {@link PhotoDao} defines and API of Data-Access Object for entity {@link Photo}
+ */
+public interface PhotoDao {
+
+ /**
+ * Saves photo into db and sets an id
+ *
+ * @param photo new photo
+ */
+ void save(Photo photo);
+
+ /**
+ * Retrieves a photo from the database by its id
+ *
+ * @param id photo id
+ * @return photo instance
+ */
+ Photo findById(long id);
+
+ /**
+ * Returns a list of all stored photos
+ *
+ * @return list of stored photos
+ */
+ List findAll();
+
+ /**
+ * Removes a photo from the database
+ *
+ * @param photo an instance of stored photo
+ */
+ void remove(Photo photo);
+
+ /**
+ * Adds a new comment to an existing photo. This method does not require additional SQL select methods to load
+ * {@link Photo}.
+ *
+ * @param photoId
+ * @param comment
+ */
+ void addComment(long photoId, String comment);
+}
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/dao/PhotoDaoImpl.java b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/dao/PhotoDaoImpl.java
new file mode 100644
index 00000000..c2f06977
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/dao/PhotoDaoImpl.java
@@ -0,0 +1,43 @@
+package com.bobocode.dao;
+
+import com.bobocode.model.Photo;
+import com.bobocode.util.ExerciseNotCompletedException;
+
+import javax.persistence.EntityManagerFactory;
+import java.util.List;
+
+/**
+ * Please note that you should not use auto-commit mode for your implementation.
+ */
+public class PhotoDaoImpl implements PhotoDao {
+ private EntityManagerFactory entityManagerFactory;
+
+ public PhotoDaoImpl(EntityManagerFactory entityManagerFactory) {
+ this.entityManagerFactory = entityManagerFactory;
+ }
+
+ @Override
+ public void save(Photo photo) {
+ throw new ExerciseNotCompletedException(); // todo
+ }
+
+ @Override
+ public Photo findById(long id) {
+ throw new ExerciseNotCompletedException(); // todo
+ }
+
+ @Override
+ public List findAll() {
+ throw new ExerciseNotCompletedException(); // todo
+ }
+
+ @Override
+ public void remove(Photo photo) {
+ throw new ExerciseNotCompletedException(); // todo
+ }
+
+ @Override
+ public void addComment(long photoId, String comment) {
+ throw new ExerciseNotCompletedException(); // todo
+ }
+}
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/model/Photo.java b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/model/Photo.java
new file mode 100644
index 00000000..cc88e862
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/model/Photo.java
@@ -0,0 +1,40 @@
+package com.bobocode.model;
+
+import com.bobocode.util.ExerciseNotCompletedException;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * todo:
+ * - make a setter for field {@link Photo#comments} {@code private}
+ * - implement equals() and hashCode() based on identifier field
+ *
+ * - configure JPA entity
+ * - specify table name: "photo"
+ * - configure auto generated identifier
+ * - configure not nullable and unique column: url
+ *
+ * - initialize field comments
+ * - map relation between Photo and PhotoComment on the child side
+ * - implement helper methods {@link Photo#addComment(PhotoComment)} and {@link Photo#removeComment(PhotoComment)}
+ * - enable cascade type {@link javax.persistence.CascadeType#ALL} for field {@link Photo#comments}
+ * - enable orphan removal
+ */
+@Getter
+@Setter
+public class Photo {
+ private Long id;
+ private String url;
+ private String description;
+ private List comments;
+
+ public void addComment(PhotoComment comment) {
+ throw new ExerciseNotCompletedException();
+ }
+
+ public void removeComment(PhotoComment comment) {
+ throw new ExerciseNotCompletedException();
+ }
+}
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/model/PhotoComment.java b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/model/PhotoComment.java
new file mode 100644
index 00000000..83edf2f7
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/java/com/bobocode/model/PhotoComment.java
@@ -0,0 +1,27 @@
+package com.bobocode.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+/**
+ * todo:
+ * - implement equals and hashCode based on identifier field
+ *
+ * - configure JPA entity
+ * - specify table name: "photo_comment"
+ * - configure auto generated identifier
+ * - configure not nullable column: text
+ *
+ * - map relation between Photo and PhotoComment using foreign_key column: "photo_id"
+ * - configure relation as mandatory (not optional)
+ */
+@Getter
+@Setter
+public class PhotoComment {
+ private Long id;
+ private String text;
+ private LocalDateTime createdOn;
+ private Photo photo;
+}
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/resources/META-INF/persistence.xml b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 00000000..35e736a7
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ com.bobocode.model.Photo
+ com.bobocode.model.PhotoComment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/PhotoCommentMappingTest.java b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/PhotoCommentMappingTest.java
new file mode 100644
index 00000000..a6f91d5a
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/PhotoCommentMappingTest.java
@@ -0,0 +1,265 @@
+package com.bobocode;
+
+import com.bobocode.model.Photo;
+import com.bobocode.model.PhotoComment;
+import com.bobocode.util.EntityManagerUtil;
+import org.junit.jupiter.api.*;
+
+import javax.persistence.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import static com.bobocode.util.PhotoTestDataGenerator.*;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class PhotoCommentMappingTest {
+ private static EntityManagerUtil emUtil;
+ private static EntityManagerFactory entityManagerFactory;
+
+ @BeforeAll
+ public static void setup() {
+ entityManagerFactory = Persistence.createEntityManagerFactory("PhotoComments");
+ emUtil = new EntityManagerUtil(entityManagerFactory);
+ }
+
+ @AfterAll
+ static void destroy() {
+ entityManagerFactory.close();
+ }
+
+ @Test
+ @Order(1)
+ @DisplayName("Comments list is initialized")
+ public void commentsListIsInitialized() {
+ Photo photo = new Photo();
+ List comments = photo.getComments();
+
+ assertThat(comments).isNotNull();
+ }
+
+ @Test
+ @Order(2)
+ @DisplayName("Setter for field \"comments\" is private in Photo entity")
+ public void commentsSetterIsPrivate() throws NoSuchMethodException {
+ Method setComments = Photo.class.getDeclaredMethod("setComments", List.class);
+
+ assertThat(setComments.getModifiers()).isEqualTo(Modifier.PRIVATE);
+ }
+
+ @Test
+ @Order(3)
+ @DisplayName("Photo table name is specified")
+ public void photoTableNameIsSpecified() {
+ Table table = Photo.class.getAnnotation(Table.class);
+ String tableName = table.name();
+
+ assertThat(tableName).isEqualTo("photo");
+ }
+
+ @Test
+ @Order(4)
+ @DisplayName("Photo comment table name is specified")
+ public void photoCommentTableNameIsSpecified() {
+ Table table = PhotoComment.class.getAnnotation(Table.class);
+
+ assertThat(table.name()).isEqualTo("photo_comment");
+ }
+
+ @Test
+ @Order(5)
+ @DisplayName("Photo URL is not null and unique")
+ public void photoUrlIsNotNullAndUnique() throws NoSuchFieldException {
+ Field url = Photo.class.getDeclaredField("url");
+ Column column = url.getAnnotation(Column.class);
+
+ assertThat(column.nullable()).isFalse();
+ assertThat(column.unique()).isTrue();
+ }
+
+ @Test
+ @Order(6)
+ @DisplayName("Photo comment text is mandatory")
+ public void photoCommentTextIsMandatory() throws NoSuchFieldException {
+ Field text = PhotoComment.class.getDeclaredField("text");
+ Column column = text.getAnnotation(Column.class);
+
+ assertThat(column.nullable()).isFalse();
+ }
+
+ @Test
+ @Order(7)
+ @DisplayName("Cascade type ALL is enabled for comments")
+ public void cascadeTypeAllIsEnabledForComments() throws NoSuchFieldException {
+ Field comments = Photo.class.getDeclaredField("comments");
+ OneToMany oneToMany = comments.getAnnotation(OneToMany.class);
+ CascadeType[] expectedCascade = {CascadeType.ALL};
+
+ assertThat(oneToMany.cascade()).isEqualTo(expectedCascade);
+ }
+
+ @Test
+ @Order(8)
+ @DisplayName("Orphan removal is enabled for comments")
+ public void orphanRemovalIsEnabledForComments() throws NoSuchFieldException {
+ Field comments = Photo.class.getDeclaredField("comments");
+ OneToMany oneToMany = comments.getAnnotation(OneToMany.class);
+
+ assertThat(oneToMany.orphanRemoval()).isTrue();
+ }
+
+ @Test
+ @Order(9)
+ @DisplayName("Foreign key column is specified")
+ public void foreignKeyColumnIsSpecified() throws NoSuchFieldException {
+ Field photo = PhotoComment.class.getDeclaredField("photo");
+ JoinColumn joinColumn = photo.getAnnotation(JoinColumn.class);
+
+ assertThat(joinColumn.name()).isEqualTo("photo_id");
+ }
+
+ @Test
+ @Order(10)
+ @DisplayName("Save a photo only")
+ public void savePhotoOnly() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ assertThat(photo.getId()).isNotNull();
+ }
+
+ @Test
+ @Order(11)
+ @DisplayName("Save a photo comment only")
+ public void savePhotoCommentOnly() {
+ PhotoComment photoComment = createRandomPhotoComment();
+
+ assertThatExceptionOfType(PersistenceException.class).isThrownBy(() ->
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photoComment)));
+ }
+
+ @Test
+ @Order(12)
+ @DisplayName("A comment cannot exist without a photo")
+ public void commentCannotExistsWithoutPhoto() throws NoSuchFieldException {
+ Field photo = PhotoComment.class.getDeclaredField("photo");
+ ManyToOne manyToOne = photo.getAnnotation(ManyToOne.class);
+
+ assertThat(manyToOne.optional()).isFalse();
+ }
+
+ @Test
+ @Order(13)
+ @DisplayName("Save a new comment")
+ public void saveNewComment() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ PhotoComment photoComment = createRandomPhotoComment();
+ photoComment.setPhoto(photo);
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photoComment));
+
+ assertThat(photoComment.getId()).isNotNull();
+ emUtil.performWithinTx(entityManager -> {
+ PhotoComment managedPhotoComment = entityManager.find(PhotoComment.class, photoComment.getId());
+ assertThat(managedPhotoComment.getPhoto()).isEqualTo(photo);
+ });
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ assertThat(managedPhoto.getComments()).contains(photoComment);
+ });
+ }
+
+ @Test
+ @Order(14)
+ @DisplayName("Add a new comment")
+ public void addNewComment() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ PhotoComment photoComment = createRandomPhotoComment();
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ managedPhoto.addComment(photoComment);
+ });
+
+ assertThat(photoComment.getId()).isNotNull();
+ emUtil.performWithinTx(entityManager -> {
+ PhotoComment managedPhotoComment = entityManager.find(PhotoComment.class, photoComment.getId());
+ assertThat(managedPhotoComment.getPhoto()).isEqualTo(photo);
+ });
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ assertThat(managedPhoto.getComments()).contains(photoComment);
+ });
+ }
+
+ @Test
+ @Order(15)
+ @DisplayName("Save new comments")
+ public void saveNewComments() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ List listOfComments = createListOfRandomComments(5);
+ listOfComments.forEach(comment -> comment.setPhoto(photo));
+
+ emUtil.performWithinTx(entityManager -> listOfComments.forEach(entityManager::persist));
+
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ assertThat(managedPhoto.getComments()).containsExactlyInAnyOrderElementsOf(listOfComments);
+ });
+ }
+
+ @Test
+ @Order(16)
+ @DisplayName("Add a new comment")
+ public void addNewComments() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+ List listOfComments = createListOfRandomComments(5);
+
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ listOfComments.forEach(managedPhoto::addComment);
+ });
+
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ assertThat(managedPhoto.getComments()).containsExactlyInAnyOrderElementsOf(listOfComments);
+ });
+ }
+
+ @Test
+ @Order(17)
+ @DisplayName("Remove a comment")
+ public void removeComment() {
+ Photo photo = createRandomPhoto();
+ PhotoComment photoComment = createRandomPhotoComment();
+ List commentList = createListOfRandomComments(5);
+ photo.addComment(photoComment);
+ commentList.forEach(photo::addComment);
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ PhotoComment managedComment = entityManager.find(PhotoComment.class, photoComment.getId());
+ managedPhoto.removeComment(managedComment);
+ });
+
+
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ PhotoComment managedPhotoComment = entityManager.find(PhotoComment.class, photoComment.getId());
+
+ assertThat(managedPhoto.getComments()).doesNotContain(photoComment);
+ assertThat(managedPhoto.getComments()).containsExactlyInAnyOrderElementsOf(commentList);
+ assertThat(managedPhotoComment).isNull();
+ });
+ }
+}
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/PhotoDaoTest.java b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/PhotoDaoTest.java
new file mode 100644
index 00000000..42223914
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/PhotoDaoTest.java
@@ -0,0 +1,99 @@
+package com.bobocode;
+
+import com.bobocode.dao.PhotoDao;
+import com.bobocode.dao.PhotoDaoImpl;
+import com.bobocode.model.Photo;
+import com.bobocode.util.EntityManagerUtil;
+import org.junit.jupiter.api.*;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import java.util.List;
+
+import static com.bobocode.util.PhotoTestDataGenerator.createListOfRandomPhotos;
+import static com.bobocode.util.PhotoTestDataGenerator.createRandomPhoto;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class PhotoDaoTest {
+ private EntityManagerUtil emUtil;
+ private PhotoDao photoDao;
+ private EntityManagerFactory entityManagerFactory;
+
+ @BeforeEach
+ public void setup() {
+ entityManagerFactory = Persistence.createEntityManagerFactory("PhotoComments");
+ emUtil = new EntityManagerUtil(entityManagerFactory);
+ photoDao = new PhotoDaoImpl(entityManagerFactory);
+ }
+
+ @AfterEach
+ public void destroy() {
+ entityManagerFactory.close();
+ }
+
+ @Test
+ @Order(1)
+ @DisplayName("Save a photo")
+ public void savePhoto() {
+ Photo photo = createRandomPhoto();
+
+ photoDao.save(photo);
+
+ Photo fountPhoto = emUtil.performReturningWithinTx(entityManager -> entityManager.find(Photo.class, photo.getId()));
+ assertThat(fountPhoto).isEqualTo(photo);
+ }
+
+ @Test
+ @Order(3)
+ @DisplayName("Find a photo by Id")
+ public void findPhotoById() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ Photo foundPhoto = photoDao.findById(photo.getId());
+
+ assertThat(foundPhoto).isEqualTo(photo);
+ }
+
+ @Test
+ @Order(3)
+ @DisplayName("Find all photos")
+ public void findAllPhotos() {
+ List listOfRandomPhotos = createListOfRandomPhotos(5);
+ emUtil.performWithinTx(entityManager -> listOfRandomPhotos.forEach(entityManager::persist));
+
+ List foundPhotos = photoDao.findAll();
+
+ assertThat(foundPhotos).containsExactlyInAnyOrderElementsOf(listOfRandomPhotos);
+ }
+
+ @Test
+ @Order(4)
+ @DisplayName("Remove a photo")
+ public void removePhoto() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ photoDao.remove(photo);
+
+ Photo removedPhoto = emUtil.performReturningWithinTx(entityManager -> entityManager.find(Photo.class, photo.getId()));
+ assertThat(removedPhoto).isNull();
+ }
+
+ @Test
+ @Order(5)
+ @DisplayName("Add a photo comment")
+ public void addPhotoComment() {
+ Photo photo = createRandomPhoto();
+ emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
+
+ photoDao.addComment(photo.getId(), "Nice picture!");
+
+ emUtil.performWithinTx(entityManager -> {
+ Photo managedPhoto = entityManager.find(Photo.class, photo.getId());
+ assertThat(managedPhoto.getComments()).extracting("text").contains("Nice picture!");
+ });
+ }
+}
diff --git a/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/util/PhotoTestDataGenerator.java b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/util/PhotoTestDataGenerator.java
new file mode 100644
index 00000000..6149e23c
--- /dev/null
+++ b/3-0-jpa-and-hibernate/3-2-2-photo-comment-dao/src/test/java/com/bobocode/util/PhotoTestDataGenerator.java
@@ -0,0 +1,35 @@
+package com.bobocode.util;
+
+import com.bobocode.model.Photo;
+import com.bobocode.model.PhotoComment;
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.toList;
+
+public class PhotoTestDataGenerator {
+ public static Photo createRandomPhoto(){
+ Photo photo = new Photo();
+ photo.setUrl(RandomStringUtils.randomAlphabetic(30));
+ photo.setDescription(RandomStringUtils.randomAlphabetic(50));
+ return photo;
+ }
+
+ public static PhotoComment createRandomPhotoComment() {
+ PhotoComment photoComment = new PhotoComment();
+ photoComment.setCreatedOn(LocalDateTime.now());
+ photoComment.setText(RandomStringUtils.randomAlphabetic(50));
+ return photoComment;
+ }
+
+ public static List createListOfRandomPhotos(int size) {
+ return Stream.generate(PhotoTestDataGenerator::createRandomPhoto).limit(size).collect(toList());
+ }
+ public static List createListOfRandomComments(int size) {
+ return Stream.generate(PhotoTestDataGenerator::createRandomPhotoComment).limit(size).collect(toList());
+ }
+}
+
diff --git a/3-0-jpa-and-hibernate/pom.xml b/3-0-jpa-and-hibernate/pom.xml
index 584ef4ed..add27038 100644
--- a/3-0-jpa-and-hibernate/pom.xml
+++ b/3-0-jpa-and-hibernate/pom.xml
@@ -20,6 +20,7 @@
3-1-1-employee-profile
3-1-2-company-products
3-1-3-author-book
+ 3-2-2-photo-comment-dao