diff --git a/README.md b/README.md
index e2cfdc7..0a53118 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,19 @@
# OtusSpringHW-Library
-Домашние задания по Spring для Otus с базами данных.
+
+Домашнее задание #6:
+- Переписать приложение для хранения книг на ORM.
+- Использовать JPA, Hibernate только в качестве JPA-провайдера.
+- Добавить комментарии к книгам, и высокоуровневые сервисы, оставляющие комментарии к книгам.
+- Покрыть DAO тестами используя H2 базу данных и соответствующий H2 Hibernate-диалект.
+
+### 19-06-19 Доработки:
+- Добавлены названия тэстов.
+- Связи OneToOne изменены на ManyToOne.
+
+### 19-06-20 Доработки:
+- Изменена струкрура запроса для сущности Book.
+
+### 19-06-22 Доработки:
+- Добавлена сущность Comment.
+- Изменение механизма добавления/обновления сущностей.
+- Добавление классов для обработки сущности Comment и покрытие тестами.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f1fbb76
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,168 @@
+
+
+ 4.0.0
+
+ home-work
+
+ ru.otus.mkulikov
+ home-work
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.3.RELEASE
+
+
+
+
+ UTF-8
+
+
+
+
+ net.sf.opencsv
+ opencsv
+ 2.3
+
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.4
+ provided
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.3.2
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.3.2
+ test
+
+
+
+ org.junit.platform
+ junit-platform-launcher
+ 1.4.1
+ test
+
+
+
+
+ org.mockito
+ mockito-all
+ 1.10.19
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.shell
+ spring-shell-starter
+ 2.0.0.RELEASE
+
+
+
+
+ com.h2database
+ h2
+ 1.4.199
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+ ${encoding}
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.0
+
+
+ false
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+ src/main/resources
+
+ **/*.csv
+ **/*.properties
+ **/*.txt
+ **/*.yml
+ **/*.sql
+
+ false
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/ru/otus/mkulikov/Application.java b/src/main/java/ru/otus/mkulikov/Application.java
new file mode 100644
index 0000000..005151d
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/Application.java
@@ -0,0 +1,22 @@
+package ru.otus.mkulikov;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 15:27
+ */
+
+@SpringBootApplication
+@EnableConfigurationProperties
+public class Application {
+
+ public static void main(String[] args) throws Exception {
+ SpringApplication.run(Application.class);
+ //Console.main(args);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/AuthorDao.java b/src/main/java/ru/otus/mkulikov/app/dao/AuthorDao.java
new file mode 100644
index 0000000..6f09bc7
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/AuthorDao.java
@@ -0,0 +1,23 @@
+package ru.otus.mkulikov.app.dao;
+
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:06
+ */
+
+public interface AuthorDao {
+
+ T getById(long id);
+
+ List getAllObjects();
+
+ int save(T t);
+
+ int deleteObject(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/AuthorDaoJpa.java b/src/main/java/ru/otus/mkulikov/app/dao/AuthorDaoJpa.java
new file mode 100644
index 0000000..9c8cdb4
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/AuthorDaoJpa.java
@@ -0,0 +1,64 @@
+package ru.otus.mkulikov.app.dao;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.mkulikov.app.model.Author;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:06
+ */
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+@Transactional
+@RequiredArgsConstructor
+public class AuthorDaoJpa implements AuthorDao {
+
+ @PersistenceContext
+ private EntityManager em;
+
+ @Override
+ public Author getById(long id) {
+ List list = em.createQuery("select a from Author a where a.id = :id", Author.class)
+ .setParameter("id", id)
+ .getResultList();
+
+ em.clear();
+ return (list != null && !list.isEmpty()) ? list.get(0) : null;
+ }
+
+ @Override
+ public List getAllObjects() {
+ List list = em.createQuery("select a from Author a order by a.id", Author.class)
+ .getResultList();
+
+ em.clear();
+ return list;
+ }
+
+ @Override
+ public int save(Author author) {
+ if (author.getId() == 0) {
+ em.persist(author);
+ } else {
+ em.merge(author);
+ }
+ System.out.println("Author saved with id: " + author.getId());
+ return 1;
+ }
+
+ @Override
+ public int deleteObject(long id) {
+ return em.createQuery("delete from Author a where a.id = :id ")
+ .setParameter("id", id)
+ .executeUpdate();
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/BookDao.java b/src/main/java/ru/otus/mkulikov/app/dao/BookDao.java
new file mode 100644
index 0000000..58aaf0f
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/BookDao.java
@@ -0,0 +1,23 @@
+package ru.otus.mkulikov.app.dao;
+
+import ru.otus.mkulikov.app.model.Book;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 13:28
+ */
+
+public interface BookDao {
+
+ T getById(long id);
+
+ List getAllObjects();
+
+ int save(T t);
+
+ int deleteObject(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/BookDaoJpa.java b/src/main/java/ru/otus/mkulikov/app/dao/BookDaoJpa.java
new file mode 100644
index 0000000..dad9f26
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/BookDaoJpa.java
@@ -0,0 +1,75 @@
+package ru.otus.mkulikov.app.dao;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.mkulikov.app.model.Book;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 13:28
+ */
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+@Transactional
+@RequiredArgsConstructor
+public class BookDaoJpa implements BookDao {
+
+ @PersistenceContext
+ private EntityManager em;
+
+ @Override
+ public Book getById(long id) {
+ List books = em.createQuery("select distinct b "
+ + "from Book b "
+ + "inner join fetch b.author a "
+ + "inner join fetch b.genre g "
+ + "left join b.comments c "
+ + "where b.id = :id ", Book.class)
+ .setParameter("id", id)
+ .getResultList();
+
+ em.clear();
+ return (books != null) ? books.get(0) : null;
+ }
+
+ @Override
+ public List getAllObjects() {
+ List books = em.createQuery("select distinct b "
+ + "from Book b "
+ + "inner join fetch b.author a "
+ + "inner join fetch b.genre g "
+ + "left join b.comments c "
+ + "order by b.id ", Book.class)
+ .getResultList();
+
+ em.clear();
+ return books;
+ }
+
+ @Override
+ public int save(Book book) {
+ if (book.getId() == 0) {
+ em.persist(book);
+ } else {
+ em.merge(book);
+ }
+ System.out.println("Book saved with id: " + book.getId());
+ return 1;
+ }
+
+ @Override
+ public int deleteObject(long id) {
+ return em.createQuery("delete from Book b where b.id = :id ")
+ .setParameter("id", id)
+ .executeUpdate();
+ }
+}
+
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/CommentDao.java b/src/main/java/ru/otus/mkulikov/app/dao/CommentDao.java
new file mode 100644
index 0000000..49ddac6
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/CommentDao.java
@@ -0,0 +1,26 @@
+package ru.otus.mkulikov.app.dao;
+
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 20.06.2019
+ * Time: 23:48
+ */
+
+public interface CommentDao {
+
+ T getById(long id);
+
+ List getByBook(Book book);
+
+ int save(T t);
+
+ List getAllObjects();
+
+ int deleteObject(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/CommentDaoJpa.java b/src/main/java/ru/otus/mkulikov/app/dao/CommentDaoJpa.java
new file mode 100644
index 0000000..94a313d
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/CommentDaoJpa.java
@@ -0,0 +1,80 @@
+package ru.otus.mkulikov.app.dao;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:06
+ */
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+@Transactional
+@RequiredArgsConstructor
+public class CommentDaoJpa implements CommentDao {
+
+ @PersistenceContext
+ private EntityManager em;
+
+ @Override
+ public Comment getById(long id) {
+ List list = em.createQuery(
+ "select c " +
+ "from Comment c " +
+ "inner join fetch c.book b " +
+ "where c.id = :id", Comment.class)
+ .setParameter("id", id)
+ .getResultList();
+
+ em.clear();
+ return (list != null && !list.isEmpty()) ? list.get(0) : null;
+ }
+
+ @Override
+ public List getByBook(Book book) {
+ List list = em.createQuery(
+ "select c " +
+ "from Comment c " +
+ "inner join fetch c.book b " +
+ "where c.book = :book", Comment.class)
+ .setParameter("book", book)
+ .getResultList();
+
+ em.clear();
+ return list;
+ }
+
+ @Override
+ public int save(Comment comment) {
+ if (comment.getId() == 0) {
+ em.persist(comment);
+ } else {
+ em.merge(comment);
+ }
+ System.out.println("Comment saved with id: " + comment.getId());
+ return 1;
+ }
+
+ @Override
+ public List getAllObjects() {
+ return em.createQuery("select a from Comment a order by a.id", Comment.class)
+ .getResultList();
+ }
+
+ @Override
+ public int deleteObject(long id) {
+ return em.createQuery("delete from Comment a where a.id = :id ")
+ .setParameter("id", id)
+ .executeUpdate();
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/GenreDao.java b/src/main/java/ru/otus/mkulikov/app/dao/GenreDao.java
new file mode 100644
index 0000000..4121f3f
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/GenreDao.java
@@ -0,0 +1,23 @@
+package ru.otus.mkulikov.app.dao;
+
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:07
+ */
+
+public interface GenreDao {
+
+ T getById(long id);
+
+ List getAllObjects();
+
+ int save(T t);
+
+ int deleteObject(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/GenreDaoJpa.java b/src/main/java/ru/otus/mkulikov/app/dao/GenreDaoJpa.java
new file mode 100644
index 0000000..f487fd2
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/GenreDaoJpa.java
@@ -0,0 +1,70 @@
+package ru.otus.mkulikov.app.dao;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.mkulikov.app.model.Genre;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:07
+ */
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "ConstantConditions", "SqlDialectInspection"})
+@Repository
+@Transactional
+@RequiredArgsConstructor
+public class GenreDaoJpa implements GenreDao {
+
+ @PersistenceContext
+ private EntityManager em;
+
+ @Override
+ public Genre getById(long id) {
+ List list = em.createQuery(
+ "select g " +
+ "from Genre g " +
+ "where g.id = :id ", Genre.class)
+ .setParameter("id", id)
+ .getResultList();
+
+ em.clear();
+ return (list != null && !list.isEmpty()) ? list.get(0) : null;
+ }
+
+ @Override
+ public List getAllObjects() {
+ List list = em.createQuery(
+ "select g " +
+ "from Genre g " +
+ "order by g.id", Genre.class)
+ .getResultList();
+
+ em.clear();
+ return list;
+ }
+
+ @Override
+ public int save(Genre genre) {
+ if (genre.getId() == 0) {
+ em.persist(genre);
+ } else {
+ em.merge(genre);
+ }
+ System.out.println("Genre saved with id: " + genre.getId());
+ return 1;
+ }
+
+ @Override
+ public int deleteObject(long id) {
+ return em.createQuery("delete from Genre g where g.id = :id ")
+ .setParameter("id", id)
+ .executeUpdate();
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Author.java b/src/main/java/ru/otus/mkulikov/app/model/Author.java
new file mode 100644
index 0000000..edca570
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Author.java
@@ -0,0 +1,57 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:02
+ */
+
+@Data
+@Entity
+@Table(name = "AUTHOR")
+public class Author {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_author")
+ @SequenceGenerator(name = "sq_author", sequenceName = "sq_author", allocationSize = 1)
+ @Column(name = "ID")
+ private long id;
+ @Column(name = "SURNAME")
+ private String surname;
+ @Column(name = "FIRST_NAME")
+ private String firstName;
+ @Column(name = "SECOND_NAME")
+ private String secondName;
+
+ public Author() {
+ }
+
+ public Author(long id, String surname, String firstName, String secondName) {
+ this.id = id;
+ this.surname = surname;
+ this.firstName = firstName;
+ this.secondName = secondName;
+ }
+
+ public Author(String surname, String firstName, String secondName) {
+ this.id = 0L;
+ this.surname = surname;
+ this.firstName = firstName;
+ this.secondName = secondName;
+ }
+
+ @Override
+ public String toString() {
+ return "Author{" +
+ "id=" + id +
+ ", surname='" + surname + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", secondName='" + secondName + '\'' +
+ "}\n";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Book.java b/src/main/java/ru/otus/mkulikov/app/model/Book.java
new file mode 100644
index 0000000..7586ff6
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Book.java
@@ -0,0 +1,90 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+import org.hibernate.annotations.Fetch;
+import org.hibernate.annotations.FetchMode;
+import org.hibernate.annotations.OnDelete;
+import org.hibernate.annotations.OnDeleteAction;
+
+import javax.persistence.*;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 13:28
+ */
+
+@Data
+@Entity
+@Table(name = "BOOK")
+public class Book {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_book")
+ @SequenceGenerator(name = "sq_book", sequenceName = "sq_book", allocationSize = 1)
+ @Column(name = "ID")
+ private long id;
+ @Column(name = "ADD_RECORD_DATE")
+ private Date addRecordDate;
+ @Column(name = "CAPTION")
+ private String caption;
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+ @Fetch(FetchMode.JOIN)
+ @ManyToOne(optional=false, fetch = FetchType.EAGER)
+ private Author author;
+
+ @Fetch(FetchMode.JOIN)
+ @ManyToOne(optional=false, fetch = FetchType.EAGER)
+ private Genre genre;
+
+ @Fetch(FetchMode.JOIN)
+ @OneToMany(mappedBy = "book", fetch = FetchType.LAZY)
+ @OnDelete(action = OnDeleteAction.CASCADE)
+ private List comments;
+
+ public Book() {
+ }
+
+ public Book(long id, Date addRecordDate, String caption, Author author, Genre genre, String description) {
+ this.id = id;
+ this.addRecordDate = addRecordDate;
+ this.author = author;
+ this.genre = genre;
+ this.caption = caption;
+ this.description = description;
+ }
+
+ public Book(long id, String caption, Author author, Genre genre, String description) {
+ this.id = id;
+ this.caption = caption;
+ this.author = author;
+ this.genre = genre;
+ this.description = description;
+ }
+
+ public Book(String caption, Author author, Genre genre, String description) {
+ this.id = 0L;
+ this.caption = caption;
+ this.author = author;
+ this.genre = genre;
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return "Book{" +
+ "id=" + id +
+ ", addRecordDate=" + addRecordDate +
+ ", caption='" + caption + '\'' +
+ ", description=" + description +
+ ", author=" + author +
+ ", genre=" + genre +
+ ", comments=" + (comments != null ? comments : "none") +
+ "}\n";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Comment.java b/src/main/java/ru/otus/mkulikov/app/model/Comment.java
new file mode 100644
index 0000000..dd7a0a5
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Comment.java
@@ -0,0 +1,67 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+import org.hibernate.annotations.Fetch;
+import org.hibernate.annotations.FetchMode;
+
+import javax.persistence.*;
+import java.util.Date;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 20.06.2019
+ * Time: 23:38
+ */
+
+@Data
+@Entity
+@Table(name = "COMMENT")
+public class Comment {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_comment")
+ @SequenceGenerator(name = "sq_comment", sequenceName = "sq_comment", allocationSize = 1)
+ @Column(name = "ID")
+ private long id;
+ @Column(name = "ADD_RECORD_DATE")
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date addRecordDate;
+ @Column(name = "USER_NAME")
+ private String userName;
+ @Column(name = "TEXT")
+ private String text;
+
+ @Fetch(FetchMode.JOIN)
+ @ManyToOne(optional=false, fetch = FetchType.EAGER)
+ private Book book;
+
+ public Comment() {
+ }
+
+ public Comment(long id, Book book, Date addRecordDate, String userName, String text) {
+ this.id = id;
+ this.book = book;
+ this.addRecordDate = addRecordDate;
+ this.userName = userName;
+ this.text = text;
+ }
+
+ public Comment(Book book, Date addRecordDate, String userName, String text) {
+ this.id = 0L;
+ this.book = book;
+ this.addRecordDate = addRecordDate;
+ this.userName = userName;
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return "Comment{" +
+ "id=" + id +
+ ", addRecordDate=" + addRecordDate +
+ ", userName=" + userName +
+ ", text='" + text + '\'' +
+ "}\n";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Genre.java b/src/main/java/ru/otus/mkulikov/app/model/Genre.java
new file mode 100644
index 0000000..dc37847
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Genre.java
@@ -0,0 +1,47 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:04
+ */
+
+@Data
+@Entity
+@Table(name = "GENRE")
+public class Genre {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_genre")
+ @SequenceGenerator(name = "sq_genre", sequenceName = "sq_genre", allocationSize = 1)
+ @Column(name = "ID")
+ private long id;
+ @Column(name = "NAME")
+ private String name;
+
+ public Genre() {
+ }
+
+ public Genre(long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Genre(String name) {
+ this.id = 0L;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Genre{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ "}\n";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/otus/mkulikov/app/service/AuthorManageService.java b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageService.java
new file mode 100644
index 0000000..2ebc5f1
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageService.java
@@ -0,0 +1,25 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:59
+ */
+
+public interface AuthorManageService {
+
+ T getAuthorById(long id);
+
+ List getAuthors();
+
+ int addAuthor(String surname, String firstName, String secondName);
+
+ int updateAuthor(long id, String surname, String firstName, String secondName);
+
+ int deleteAuthor(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/AuthorManageServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageServiceImpl.java
new file mode 100644
index 0000000..f0bd226
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageServiceImpl.java
@@ -0,0 +1,47 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.AuthorDao;
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:53
+ */
+
+@Service
+@RequiredArgsConstructor
+public class AuthorManageServiceImpl implements AuthorManageService {
+
+ private final AuthorDao authorDao;
+
+ @Override
+ public Author getAuthorById(long id) {
+ return authorDao.getById(id);
+ }
+
+ @Override
+ public List getAuthors() {
+ return authorDao.getAllObjects();
+ }
+
+ @Override
+ public int addAuthor(String surname, String firstName, String secondName) {
+ return authorDao.save(new Author(surname, firstName, secondName));
+ }
+
+ @Override
+ public int updateAuthor(long id, String surname, String firstName, String secondName) {
+ return authorDao.save(new Author(id, surname, firstName, secondName));
+ }
+
+ @Override
+ public int deleteAuthor(long id) {
+ return authorDao.deleteObject(id);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/BookManageSevice.java b/src/main/java/ru/otus/mkulikov/app/service/BookManageSevice.java
new file mode 100644
index 0000000..e08867d
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/BookManageSevice.java
@@ -0,0 +1,25 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Book;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 27.05.2019
+ * Time: 13:42
+ */
+
+public interface BookManageSevice {
+
+ T getBookById(long id);
+
+ List getBooks();
+
+ int addBook(String caption, int authorId, int genreId, String description);
+
+ int updateBook(long id, String caption, int authorId, int genreId, String description);
+
+ int deleteBook(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/BookManageSeviceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/BookManageSeviceImpl.java
new file mode 100644
index 0000000..94a97f1
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/BookManageSeviceImpl.java
@@ -0,0 +1,59 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.AuthorDao;
+import ru.otus.mkulikov.app.dao.BookDao;
+import ru.otus.mkulikov.app.dao.GenreDao;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 27.05.2019
+ * Time: 13:42
+ */
+
+@Service
+@RequiredArgsConstructor
+public class BookManageSeviceImpl implements BookManageSevice {
+
+ private final BookDao bookDao;
+ private final AuthorDao authorDao;
+ private final GenreDao genreDao;
+
+ @Override
+ public Book getBookById(long id) {
+ return bookDao.getById(id);
+ }
+
+ @Override
+ public List getBooks() {
+ return bookDao.getAllObjects();
+ }
+
+ @Override
+ public int addBook(String caption, int authorId, int genreId, String description) {
+ Author author = authorDao.getById(authorId);
+ Genre genre = genreDao.getById(genreId);
+
+ return bookDao.save(new Book(caption, author, genre, description));
+ }
+
+ @Override
+ public int updateBook(long id, String caption, int authorId, int genreId, String description) {
+ Author author = authorDao.getById(authorId);
+ Genre genre = genreDao.getById(genreId);
+
+ return bookDao.save(new Book(id, caption, author, genre, description));
+ }
+
+ @Override
+ public int deleteBook(long id) {
+ return bookDao.deleteObject(id);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/CommentManageService.java b/src/main/java/ru/otus/mkulikov/app/service/CommentManageService.java
new file mode 100644
index 0000000..5c9e5e7
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/CommentManageService.java
@@ -0,0 +1,26 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Comment;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:50
+ */
+public interface CommentManageService {
+
+ T getCommentById(long id);
+
+ List getComments();
+
+ List getCommentsByBookId(long bookId);
+
+ int addComment(long bookId, String userName, String text);
+
+ int updateComment(long id, String userName, String text);
+
+ int deleteComment(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/CommentManageServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/CommentManageServiceImpl.java
new file mode 100644
index 0000000..591e132
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/CommentManageServiceImpl.java
@@ -0,0 +1,65 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.BookDao;
+import ru.otus.mkulikov.app.dao.CommentDao;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:52
+ */
+
+@Service
+@RequiredArgsConstructor
+public class CommentManageServiceImpl implements CommentManageService {
+
+ private final CommentDao commentDao;
+ private final BookDao bookDao;
+
+ @Override
+ public Comment getCommentById(long id) {
+ return commentDao.getById(id);
+ }
+
+ @Override
+ public List getComments() {
+ return commentDao.getAllObjects();
+ }
+
+ @Override
+ public List getCommentsByBookId(long bookId) {
+ Book book = bookDao.getById(bookId);
+
+ return commentDao.getByBook(book);
+ }
+
+ @Override
+ public int addComment(long bookId, String userName, String text) {
+ Book book = bookDao.getById(bookId);
+
+ return commentDao.save(new Comment(book, new Date(), userName, text));
+ }
+
+ @Override
+ public int updateComment(long id, String userName, String text) {
+ Comment comment = commentDao.getById(id);
+ comment.setAddRecordDate(new Date());
+ comment.setUserName(userName);
+ comment.setText(text);
+
+ return commentDao.save(comment);
+ }
+
+ @Override
+ public int deleteComment(long id) {
+ return commentDao.deleteObject(id);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/GenreManageService.java b/src/main/java/ru/otus/mkulikov/app/service/GenreManageService.java
new file mode 100644
index 0000000..80015b3
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/GenreManageService.java
@@ -0,0 +1,24 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:50
+ */
+public interface GenreManageService {
+
+ T getGenreById(long id);
+
+ List getGenres();
+
+ int addGenre(String name);
+
+ int updateGenre(long id, String name);
+
+ int deleteGenre(long id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/GenreManageServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/GenreManageServiceImpl.java
new file mode 100644
index 0000000..2cf897a
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/GenreManageServiceImpl.java
@@ -0,0 +1,47 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.GenreDao;
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:52
+ */
+
+@Service
+@RequiredArgsConstructor
+public class GenreManageServiceImpl implements GenreManageService {
+
+ private final GenreDao genreDao;
+
+ @Override
+ public Genre getGenreById(long id) {
+ return genreDao.getById(id);
+ }
+
+ @Override
+ public List getGenres() {
+ return genreDao.getAllObjects();
+ }
+
+ @Override
+ public int addGenre(String name) {
+ return genreDao.save(new Genre(name));
+ }
+
+ @Override
+ public int updateGenre(long id, String name) {
+ return genreDao.save(new Genre(id, name));
+ }
+
+ @Override
+ public int deleteGenre(long id) {
+ return genreDao.deleteObject(id);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/utils/DateUtil.java b/src/main/java/ru/otus/mkulikov/app/utils/DateUtil.java
new file mode 100644
index 0000000..ffbce20
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/utils/DateUtil.java
@@ -0,0 +1,29 @@
+package ru.otus.mkulikov.app.utils;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 05.06.2019
+ * Time: 11:33
+ */
+
+public class DateUtil {
+
+ private final static String DATE_FORMAT = "yyyy-MM-dd";
+
+ private final static String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ public static String dateToString(Date date) {
+ DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+ return date != null ? dateFormat.format(date) : null;
+ }
+
+ public static String dateTimeToString(Date date) {
+ DateFormat dateFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
+ return date != null ? dateFormat.format(date) : null;
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/AuthorCommands.java b/src/main/java/ru/otus/mkulikov/shell/AuthorCommands.java
new file mode 100644
index 0000000..2ace11e
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/AuthorCommands.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.service.AuthorManageService;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:46
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class AuthorCommands {
+
+ private final AuthorManageService authorManageService;
+
+ @ShellMethod(key = {"getAuthorById"}, value = "Select author by id.")
+ public String getAuthorById(@ShellOption long id) {
+ Author author = authorManageService.getAuthorById(id);
+ return author.toString();
+ }
+
+ @ShellMethod(key = {"getAuthors"}, value = "Select all authors.")
+ public String getAuthors() {
+ List allObjects = authorManageService.getAuthors();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addAuthor"}, value = "Add new author.")
+ public String addAuthor(@ShellOption String surname, @ShellOption String firstName, @ShellOption String secondName) {
+ int count = authorManageService.addAuthor(surname, firstName, secondName);
+ return "Add " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"updateAuthor"}, value = "Update author by id.")
+ public String updateAuthor(@ShellOption long id, @ShellOption String surname, @ShellOption String firstName, @ShellOption String secondName) {
+ int count = authorManageService.updateAuthor(id, surname, firstName, secondName);
+ return "Updated " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"deleteAuthor"}, value = "Delete author by id.")
+ public String deleteAuthor(@ShellOption long id) {
+ int count = authorManageService.deleteAuthor(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/BookCommands.java b/src/main/java/ru/otus/mkulikov/shell/BookCommands.java
new file mode 100644
index 0000000..b5119f5
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/BookCommands.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.service.BookManageSevice;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:33
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class BookCommands {
+
+ private final BookManageSevice bookManageSevice;
+
+ @ShellMethod(key = {"getBookById"}, value = "Select book by id.")
+ public String getBookById(@ShellOption long id) {
+ Book book = bookManageSevice.getBookById(id);
+ return book.toString();
+ }
+
+ @ShellMethod(key = {"getBooks"}, value = "Select all books.")
+ public String getBooks() {
+ List allObjects = bookManageSevice.getBooks();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addBook"}, value = "Add new book.")
+ public String addBook(@ShellOption String caption, @ShellOption int authorId, @ShellOption int genreId, @ShellOption String description) {
+ int count = bookManageSevice.addBook(caption, authorId, genreId, description);
+ return "Add " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"updateBook"}, value = "Update book by id.")
+ public String updateBook(@ShellOption long id, @ShellOption String caption, @ShellOption int authorId, @ShellOption int genreId, @ShellOption String description) {
+ int count = bookManageSevice.updateBook(id, caption, authorId, genreId, description);
+ return "Updated " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"deleteBook"}, value = "Delete book by id.")
+ public String deleteBook(@ShellOption long id) {
+ int count = bookManageSevice.deleteBook(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/CommentCommands.java b/src/main/java/ru/otus/mkulikov/shell/CommentCommands.java
new file mode 100644
index 0000000..3965c34
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/CommentCommands.java
@@ -0,0 +1,60 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.service.CommentManageService;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:47
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class CommentCommands {
+
+ private final CommentManageService commentManageService;
+
+ @ShellMethod(key = {"getCommentById"}, value = "Select comment by id.")
+ public String getCommentById(@ShellOption long id) {
+ Comment comment = commentManageService.getCommentById(id);
+ return comment.toString();
+ }
+
+ @ShellMethod(key = {"getComments"}, value = "Select all comments.")
+ public String getComments() {
+ List allObjects = commentManageService.getComments();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"getCommentsByBookId"}, value = "Select all comments.")
+ public String getCommentsByBookId(@ShellOption long bookId) {
+ List allObjects = commentManageService.getCommentsByBookId(bookId);
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addComment"}, value = "Add new comment.")
+ public String addComment(@ShellOption long bookId, @ShellOption String userName, @ShellOption String text) {
+ int count = commentManageService.addComment(bookId, userName, text);
+ return "Add " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"updateComment"}, value = "Update comment by id.")
+ public String updateComment(@ShellOption long id, @ShellOption String userName, @ShellOption String text) {
+ int count = commentManageService.updateComment(id, userName, text);
+ return "Updated " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"deleteComment"}, value = "Delete comment by id.")
+ public String deleteComment(@ShellOption long id) {
+ int count = commentManageService.deleteComment(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/GenreCommands.java b/src/main/java/ru/otus/mkulikov/shell/GenreCommands.java
new file mode 100644
index 0000000..eb2344e
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/GenreCommands.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.service.GenreManageService;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:47
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class GenreCommands {
+
+ private final GenreManageService genreManageService;
+
+ @ShellMethod(key = {"getGenreById"}, value = "Select genre by id.")
+ public String getGenreById(@ShellOption long id) {
+ Genre genre = genreManageService.getGenreById(id);
+ return genre.toString();
+ }
+
+ @ShellMethod(key = {"getGenres"}, value = "Select all genres.")
+ public String getGenres() {
+ List allObjects = genreManageService.getGenres();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addGenre"}, value = "Add new genre.")
+ public String addGenre(@ShellOption String name) {
+ int count = genreManageService.addGenre(name);
+ return "Add " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"updateGenre"}, value = "Update genre by id.")
+ public String updateGenre(@ShellOption long id, @ShellOption String name) {
+ int count = genreManageService.updateGenre(id, name);
+ return "Updated " + count + " row(s)";
+ }
+
+ @ShellMethod(key = {"deleteGenre"}, value = "Delete genre by id.")
+ public String deleteGenre(@ShellOption long id) {
+ int count = genreManageService.deleteGenre(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..91ca387
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,16 @@
+spring:
+ banner:
+ location: "classpath:banner.txt"
+
+ datasource:
+ initialization-mode: always
+ platform: h2
+
+ jpa:
+ hibernate:
+ ddl-auto: none
+
+ properties:
+ hibernate:
+ show_sql: false
+ format_sql: false
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
new file mode 100644
index 0000000..1687640
--- /dev/null
+++ b/src/main/resources/banner.txt
@@ -0,0 +1,11 @@
+ ___ ___ ___ ___ ___
+ /\__\ /\ \ /\__\ /\ \ /\ \
+ /:/ / /::\ \ /:/ / /::\ \ /::\ \
+ /:/ / /:/\:\ \ /:/ / /:/\:\ \ /:/\ \ \
+ /:/ / ___ /:/ \:\ \ /:/ / /::\~\:\ \ _\:\~\ \ \
+ /:/__/ /\__\ /:/__/ \:\__\ /:/__/ /:/\:\ \:\__\ /\ \:\ \ \__\
+ \:\ \ /:/ / \:\ \ /:/ / \:\ \ \:\~\:\ \/__/ \:\ \:\ \/__/
+ \:\ /:/ / \:\ /:/ / \:\ \ \:\ \:\__\ \:\ \:\__\
+ \:\/:/ / \:\/:/ / \:\ \ \:\ \/__/ \:\/:/ /
+ \::/ / \::/ / \:\__\ \:\__\ \::/ /
+ \/__/ \/__/ \/__/ \/__/ \/__/
\ No newline at end of file
diff --git a/src/main/resources/data-h2.sql b/src/main/resources/data-h2.sql
new file mode 100644
index 0000000..bca97c4
--- /dev/null
+++ b/src/main/resources/data-h2.sql
@@ -0,0 +1,39 @@
+insert into AUTHOR (ID, SURNAME, FIRST_NAME, SECOND_NAME)
+values (sq_author.nextval, 'Surname1', 'FirstName1', 'SecondName1');
+
+insert into AUTHOR (ID, SURNAME, FIRST_NAME, SECOND_NAME)
+values (sq_author.nextval, 'Surname2', 'FirstName2', 'SecondName2');
+
+insert into AUTHOR (ID, SURNAME, FIRST_NAME, SECOND_NAME)
+values (sq_author.nextval, 'Surname3', 'FirstName3', 'SecondName3');
+
+--------------------------
+
+insert into GENRE (ID, NAME) values (sq_genre.nextval, 'Genre1');
+insert into GENRE (ID, NAME) values (sq_genre.nextval, 'Genre2');
+insert into GENRE (ID, NAME) values (sq_genre.nextval, 'Genre3');
+
+--------------------------
+
+insert into BOOK (ID, ADD_RECORD_DATE, CAPTION, AUTHOR_ID, GENRE_ID, DESCRIPTION)
+values (sq_book.nextval, to_date('2019-01-01','YYYY-MM-DD'), 'book_1', 1, 1, 'description');
+
+insert into BOOK (ID, ADD_RECORD_DATE, CAPTION, AUTHOR_ID, GENRE_ID, DESCRIPTION)
+values (sq_book.nextval, to_date('2019-01-01','YYYY-MM-DD'), 'book_2', 1, 1, 'description');
+
+insert into BOOK (ID, ADD_RECORD_DATE, CAPTION, AUTHOR_ID, GENRE_ID, DESCRIPTION)
+values (sq_book.nextval, to_date('2019-01-01','YYYY-MM-DD'), 'book_3', 1, 1, 'description');
+
+--------------------------
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 1, to_date('2019-01-01 10:01:01','YYYY-MM-DD HH24-MI-SS'), 'user1', 'text1');
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 1, to_date('2019-01-01 10:01:02','YYYY-MM-DD HH24-MI-SS'), 'user2', 'text2');
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 2, to_date('2019-01-01 10:01:03','YYYY-MM-DD HH24-MI-SS'), 'user3', 'text3');
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 2, to_date('2019-01-01 10:01:04','YYYY-MM-DD HH24-MI-SS'), 'user4', 'text4');
\ No newline at end of file
diff --git a/src/main/resources/schema-h2.sql b/src/main/resources/schema-h2.sql
new file mode 100644
index 0000000..7156478
--- /dev/null
+++ b/src/main/resources/schema-h2.sql
@@ -0,0 +1,43 @@
+DROP TABLE IF EXISTS BOOK;
+DROP TABLE IF EXISTS AUTHOR;
+DROP TABLE IF EXISTS GENRE;
+DROP TABLE IF EXISTS COMMENT;
+
+CREATE TABLE AUTHOR(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ SURNAME VARCHAR(100) not null,
+ FIRST_NAME VARCHAR(100) not null,
+ SECOND_NAME VARCHAR(100)
+);
+
+CREATE TABLE GENRE(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ NAME VARCHAR(100) not null
+);
+
+CREATE TABLE BOOK(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ ADD_RECORD_DATE DATE default sysdate,
+ CAPTION VARCHAR(255) not null,
+ AUTHOR_ID NUMBER(20,0) not null,
+ GENRE_ID NUMBER(20,0) not null,
+ DESCRIPTION VARCHAR(255),
+
+ foreign key (AUTHOR_ID) references AUTHOR(ID),
+ foreign key (GENRE_ID) references GENRE(ID)
+);
+
+CREATE TABLE COMMENT(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ BOOK_ID NUMBER(20,0) not null,
+ ADD_RECORD_DATE TIMESTAMP default sysdate,
+ USER_NAME VARCHAR(20),
+ TEXT VARCHAR(500),
+
+ foreign key (BOOK_ID) references BOOK(ID) ON DELETE CASCADE
+);
+
+CREATE SEQUENCE sq_author minvalue 1 start with 1 increment by 1;
+CREATE SEQUENCE sq_genre minvalue 1 start with 1 increment by 1;
+CREATE SEQUENCE sq_book minvalue 1 start with 1 increment by 1;
+CREATE SEQUENCE sq_comment minvalue 1 start with 1 increment by 1;
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/AuthorDaoJpaTest.java b/src/test/java/ru/otus/mkulikov/app/dao/AuthorDaoJpaTest.java
new file mode 100644
index 0000000..3ec510f
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/AuthorDaoJpaTest.java
@@ -0,0 +1,109 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Author;
+
+import javax.persistence.PersistenceException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 29.05.2019
+ * Time: 10:12
+ */
+
+@DisplayName("Класс AuthorDaoJpa")
+@RunWith(SpringRunner.class)
+@Import(AuthorDaoJpa.class)
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class AuthorDaoJpaTest {
+
+ @Autowired
+ private AuthorDao authorDaoJpa;
+
+ @Test
+ @DisplayName("Получение автора по id")
+ void getById() {
+ Author author = authorDaoJpa.getById(1L);
+
+ assertAll(
+ "author",
+ () -> assertNotNull(author),
+ () -> assertEquals(1L, author.getId()),
+ () -> assertEquals("Surname", author.getSurname()),
+ () -> assertEquals("FirstName", author.getFirstName()),
+ () -> assertEquals("SecondName", author.getSecondName())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех авторов")
+ void getAllObjects() {
+ List authors = authorDaoJpa.getAllObjects();
+
+ assertAll(
+ "authors",
+ () -> assertNotNull(authors),
+ () -> assertEquals(3, authors.size()),
+ () -> assertEquals("Surname", authors.get(0).getSurname()),
+ () -> assertEquals("Surname2", authors.get(1).getSurname()),
+ () -> assertEquals("Surname3", authors.get(2).getSurname())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление автора")
+ void addObject() {
+ Author author = new Author("TestSurname", "TestFirstName", "TestSecondName");
+ int count = authorDaoJpa.save(author);
+ Author author_selected = authorDaoJpa.getById(4L);
+
+ assertAll(
+ "author",
+ () -> assertNotNull(author_selected),
+ () -> assertEquals(1, count),
+ () -> assertEquals(4, author_selected.getId()),
+ () -> assertEquals(author.getSurname(), author_selected.getSurname()),
+ () -> assertEquals(author.getFirstName(), author_selected.getFirstName()),
+ () -> assertEquals(author.getSecondName(), author_selected.getSecondName())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление автора, который используется в таблице книг")
+ void deleteObject() {
+ assertThrows(PersistenceException.class, () -> { authorDaoJpa.deleteObject(1L); });
+ }
+
+ @Test
+ @DisplayName("Обновление автора")
+ void updateObject() {
+ Author author1 = authorDaoJpa.getById(1L);
+ int count = authorDaoJpa.save(
+ new Author(1L, "TestSurname", "TestFirstName", "TestSecondName")
+ );
+ Author author2 = authorDaoJpa.getById(1L);
+
+ assertAll(
+ "author",
+ () -> assertEquals(1, count),
+ () -> assertEquals("Surname", author1.getSurname()),
+ () -> assertEquals("FirstName", author1.getFirstName()),
+ () -> assertEquals("SecondName", author1.getSecondName()),
+ () -> assertEquals("TestSurname", author2.getSurname()),
+ () -> assertEquals("TestFirstName", author2.getFirstName()),
+ () -> assertEquals("TestSecondName", author2.getSecondName())
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/BookDaoJpaTest.java b/src/test/java/ru/otus/mkulikov/app/dao/BookDaoJpaTest.java
new file mode 100644
index 0000000..6c1e247
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/BookDaoJpaTest.java
@@ -0,0 +1,131 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 28.05.2019
+ * Time: 13:16
+ */
+
+@DisplayName("Класс BookDaoJpa")
+@RunWith(SpringRunner.class)
+@Import({BookDaoJpa.class, AuthorDaoJpa.class, GenreDaoJpa.class})
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class BookDaoJpaTest {
+
+ @Autowired
+ private BookDao bookDaoJpa;
+ @Autowired
+ private AuthorDao authorDao;
+ @Autowired
+ private GenreDao genreDao;
+
+ @Test
+ @DisplayName("Получение книги по id")
+ void getById() {
+ Book book = bookDaoJpa.getById(1L);
+
+ assertAll(
+ "book",
+ () -> assertNotNull(book),
+ () -> assertEquals(1L, book.getId()),
+ () -> assertEquals("2019-01-01", DateUtil.dateToString(book.getAddRecordDate())),
+ () -> assertEquals("book_1", book.getCaption()),
+ () -> assertEquals(1, book.getAuthor().getId()),
+ () -> assertEquals(1, book.getGenre().getId()),
+ () -> assertEquals("description", book.getDescription())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех книг")
+ void getAllObjects() {
+ List books = bookDaoJpa.getAllObjects();
+
+ assertAll(
+ "books",
+ () -> assertNotNull(books),
+ () -> assertEquals(3, books.size()),
+ () -> assertEquals("book_1", books.get(0).getCaption()),
+ () -> assertEquals("book_2", books.get(1).getCaption()),
+ () -> assertEquals("book_3", books.get(2).getCaption())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление книги")
+ void addObject() {
+ int count = bookDaoJpa.save(getNewBook());
+ Book book = bookDaoJpa.getById(4L);
+
+ assertAll(
+ "book",
+ () -> assertNotNull(book),
+ () -> assertEquals(1, count),
+ () -> assertEquals(4L, book.getId()),
+ () -> assertEquals("Test_Book", book.getCaption()),
+ () -> assertEquals(1, book.getAuthor().getId()),
+ () -> assertEquals(1, book.getGenre().getId()),
+ () -> assertEquals("Test_Description", book.getDescription())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление книги по id")
+ void deleteObject() {
+ int count = bookDaoJpa.deleteObject(1L);
+
+ assertAll(
+ "book",
+ () -> assertEquals(1, count),
+ () -> assertThrows(IndexOutOfBoundsException.class, () -> { bookDaoJpa.getById(1L); })
+ );
+ }
+
+ @Test
+ @DisplayName("Обновление книги")
+ void updateObject() {
+ Book book1 = bookDaoJpa.getById(1L);
+ int count = bookDaoJpa.save(getUpdatedBook());
+ Book book2 = bookDaoJpa.getById(1L);
+
+ assertAll(
+ "book",
+ () -> assertEquals(1, count),
+ () -> assertEquals("book_1", book1.getCaption()),
+ () -> assertEquals("description", book1.getDescription()),
+ () -> assertEquals("Test_Book", book2.getCaption()),
+ () -> assertEquals("Test_Description", book2.getDescription())
+ );
+ }
+
+ private Book getNewBook() {
+ Author author = authorDao.getById(1);
+ Genre genre = genreDao.getById(1);
+ return new Book("Test_Book", author, genre, "Test_Description");
+ }
+
+ private Book getUpdatedBook() {
+ Author author = authorDao.getById(1);
+ Genre genre = genreDao.getById(1);
+ return new Book(1L,"Test_Book", author, genre, "Test_Description");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/CommentDaoJpaTest.java b/src/test/java/ru/otus/mkulikov/app/dao/CommentDaoJpaTest.java
new file mode 100644
index 0000000..3336556
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/CommentDaoJpaTest.java
@@ -0,0 +1,139 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.Date;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 28.05.2019
+ * Time: 9:45
+ */
+
+@DisplayName("Класс CommentDaoJpa")
+@RunWith(SpringRunner.class)
+@Import({CommentDaoJpa.class, BookDaoJpa.class})
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class CommentDaoJpaTest {
+
+ @Autowired
+ private BookDao bookDaoJpa;
+ @Autowired
+ private CommentDao commentDaoJpa;
+
+ @Test
+ @DisplayName("Получение комментария по id")
+ void getById() {
+ Comment comment = commentDaoJpa.getById(1L);
+
+ assertAll(
+ "comment",
+ () -> assertNotNull(comment),
+ () -> assertNotNull(comment.getBook()),
+ () -> assertEquals(1L, comment.getId()),
+ () -> assertEquals("user1", comment.getUserName()),
+ () -> assertEquals("2019-01-01 10:01:01", DateUtil.dateTimeToString(comment.getAddRecordDate())),
+ () -> assertEquals("text1", comment.getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех комментариев")
+ void getAllObjects() {
+ List comments = commentDaoJpa.getAllObjects();
+
+ assertAll(
+ "comments",
+ () -> assertNotNull(comments),
+ () -> assertEquals(4, comments.size()),
+ () -> assertEquals("text1", comments.get(0).getText()),
+ () -> assertEquals("text2", comments.get(1).getText()),
+ () -> assertEquals("text3", comments.get(2).getText()),
+ () -> assertEquals("text4", comments.get(3).getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех комментариев для книги")
+ void getObjectsByBook() {
+ Book book = bookDaoJpa.getById(1L);
+ List comments = commentDaoJpa.getByBook(book);
+
+ assertAll(
+ "comments",
+ () -> assertNotNull(comments),
+ () -> assertEquals(2, comments.size()),
+ () -> assertEquals("text1", comments.get(0).getText()),
+ () -> assertEquals("text2", comments.get(1).getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление комментария")
+ void addObject() {
+ Date date = new Date();
+ Book book = bookDaoJpa.getById(1L);
+ int count = commentDaoJpa.save(new Comment(book, date, "user5", "text5"));
+
+ Comment comment = commentDaoJpa.getById(5L);
+
+ assertAll(
+ "comment",
+ () -> assertNotNull(comment),
+ () -> assertNotNull(comment.getBook()),
+ () -> assertEquals(1, count),
+ () -> assertEquals(5L, comment.getId()),
+ () -> assertEquals(date, comment.getAddRecordDate()),
+ () -> assertEquals("user5", comment.getUserName()),
+ () -> assertEquals("text5", comment.getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление комментария")
+ void deleteObject() {
+ commentDaoJpa.deleteObject(1L);
+ assertNull(commentDaoJpa.getById(1L));
+ }
+
+ @Test
+ @DisplayName("Обновление комментария")
+ void updateObject() {
+ Comment comment1 = commentDaoJpa.getById(1L);
+
+ Date date = new Date();
+ int count = commentDaoJpa.save(new Comment(1L, comment1.getBook(), date, "TestUser", "TestText"));
+
+ Comment comment2 = commentDaoJpa.getById(1L);
+
+ assertAll(
+ "comment",
+ () -> assertNotNull(comment1),
+ () -> assertNotNull(comment2),
+ () -> assertNotNull(comment1.getBook()),
+ () -> assertNotNull(comment2.getBook()),
+ () -> assertEquals(1, count),
+ () -> assertEquals("user1", comment1.getUserName()),
+ () -> assertEquals("text1", comment1.getText()),
+ () -> assertEquals(date, comment2.getAddRecordDate()),
+ () -> assertEquals("TestUser", comment2.getUserName()),
+ () -> assertEquals("TestText", comment2.getText())
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/GenreDaoJpaTest.java b/src/test/java/ru/otus/mkulikov/app/dao/GenreDaoJpaTest.java
new file mode 100644
index 0000000..a91800e
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/GenreDaoJpaTest.java
@@ -0,0 +1,99 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Genre;
+
+import javax.persistence.PersistenceException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 28.05.2019
+ * Time: 9:45
+ */
+
+@DisplayName("Класс GenreDaoJpa")
+@RunWith(SpringRunner.class)
+@Import(GenreDaoJpa.class)
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class GenreDaoJpaTest {
+
+ @Autowired
+ private GenreDao genreDaoJpa;
+
+ @Test
+ @DisplayName("Получение жанра по id")
+ void getById() {
+ Genre genre = genreDaoJpa.getById(1L);
+
+ assertAll(
+ "genre",
+ () -> assertNotNull(genre),
+ () -> assertEquals(1L, genre.getId()),
+ () -> assertEquals("Genre1", genre.getName())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех жанров")
+ void getAllObjects() {
+ List genres = genreDaoJpa.getAllObjects();
+
+ assertAll(
+ "genres",
+ () -> assertNotNull(genres),
+ () -> assertEquals(3, genres.size()),
+ () -> assertEquals("Genre1", genres.get(0).getName()),
+ () -> assertEquals("Genre2", genres.get(1).getName()),
+ () -> assertEquals("Genre3", genres.get(2).getName())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление жанра")
+ void addObject() {
+ int count = genreDaoJpa.save(new Genre("Test4"));
+
+ Genre genre = genreDaoJpa.getById(4L);
+
+ assertAll(
+ "genre",
+ () -> assertNotNull(genre),
+ () -> assertEquals(1, count),
+ () -> assertEquals(4L, genre.getId()),
+ () -> assertEquals("Test4", genre.getName())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление жанра, который используется в таблице книг")
+ void deleteObject() {
+ assertThrows(PersistenceException.class, () -> { genreDaoJpa.deleteObject(1L); });
+ }
+
+ @Test
+ @DisplayName("Обновление жанра")
+ void updateObject() {
+ Genre genre1 = genreDaoJpa.getById(1L);
+ int count = genreDaoJpa.save(new Genre(1L, "UpdatedName"));
+ Genre genre2 = genreDaoJpa.getById(1L);
+
+ assertAll(
+ "genre",
+ () -> assertEquals(1, count),
+ () -> assertEquals("Genre1", genre1.getName()),
+ () -> assertEquals("UpdatedName", genre2.getName())
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/model/DateUtilTest.java b/src/test/java/ru/otus/mkulikov/app/model/DateUtilTest.java
new file mode 100644
index 0000000..1276d90
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/model/DateUtilTest.java
@@ -0,0 +1,57 @@
+package ru.otus.mkulikov.app.model;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 29.05.2019
+ * Time: 11:39
+ */
+
+@DisplayName("Класс DateUtil")
+@RunWith(SpringRunner.class)
+class DateUtilTest {
+
+ @Test
+ @DisplayName("Формат даты 'yyyy-MM-dd'")
+ void dateToString() {
+ Book book = getNewBook();
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+ assertAll(
+ "book",
+ () -> assertNotNull(book),
+ () -> assertEquals(dateFormat.format(new Date()), DateUtil.dateToString(book.getAddRecordDate()))
+ );
+ }
+
+ @Test
+ @DisplayName("Формат даты 'yyyy-MM-dd HH:mm:ss'")
+ void dateTimeToString() {
+ Book book = getNewBook();
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ assertAll(
+ "book",
+ () -> assertNotNull(book),
+ () -> assertEquals(dateFormat.format(new Date()), DateUtil.dateTimeToString(book.getAddRecordDate()))
+ );
+ }
+
+ private Book getNewBook() {
+ Author author = new Author(1L, "TestSurname", "TestFirstName", "TestSecondName");
+ Genre genre = new Genre("Test4");
+ return new Book(1L, new Date(), "Test_Book", author, genre, "Test_Comment");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/service/AuthorManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/AuthorManageSeviceImplTest.java
new file mode 100644
index 0000000..2203f8c
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/AuthorManageSeviceImplTest.java
@@ -0,0 +1,106 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Author;
+
+import javax.persistence.PersistenceException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:59
+ */
+
+@DisplayName("Класс AuthorManageSevice")
+@RunWith(SpringRunner.class)
+@ComponentScan("ru.otus.mkulikov.app")
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class AuthorManageSeviceImplTest {
+
+ @Autowired
+ private AuthorManageService authorManageService;
+
+ @Test
+ @DisplayName("Получение автора по id")
+ void getAuthorById() {
+ Author author = authorManageService.getAuthorById(1L);
+
+ assertAll(
+ "author",
+ () -> assertNotNull(author),
+ () -> assertEquals(1L, author.getId()),
+ () -> assertEquals("Surname", author.getSurname()),
+ () -> assertEquals("FirstName", author.getFirstName()),
+ () -> assertEquals("SecondName", author.getSecondName())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех авторов")
+ void getAuthors() {
+ List authors = authorManageService.getAuthors();
+
+ assertAll(
+ "authors",
+ () -> assertNotNull(authors),
+ () -> assertEquals(3, authors.size()),
+ () -> assertEquals("Surname", authors.get(0).getSurname()),
+ () -> assertEquals("Surname2", authors.get(1).getSurname()),
+ () -> assertEquals("Surname3", authors.get(2).getSurname())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление автора")
+ void addAuthor() {
+ int count = authorManageService.addAuthor("TestSurname", "TestFirstName", "TestSecondName");
+ Author author_selected = authorManageService.getAuthorById(4L);
+
+ assertAll(
+ "author",
+ () -> assertNotNull(author_selected),
+ () -> assertEquals(1, count),
+ () -> assertEquals(4L, author_selected.getId()),
+ () -> assertEquals("TestSurname", author_selected.getSurname()),
+ () -> assertEquals("TestFirstName", author_selected.getFirstName()),
+ () -> assertEquals("TestSecondName", author_selected.getSecondName())
+ );
+ }
+
+ @Test
+ @DisplayName("Обновление автора")
+ void updateAuthor() {
+ Author author1 = authorManageService.getAuthorById(1L);
+ int count = authorManageService.updateAuthor(1L, "TestSurname", "TestFirstName", "TestSecondName");
+ Author author2 = authorManageService.getAuthorById(1L);
+
+ assertAll(
+ "author",
+ () -> assertEquals(1, count),
+ () -> assertEquals("Surname", author1.getSurname()),
+ () -> assertEquals("FirstName", author1.getFirstName()),
+ () -> assertEquals("SecondName", author1.getSecondName()),
+ () -> assertEquals("TestSurname", author2.getSurname()),
+ () -> assertEquals("TestFirstName", author2.getFirstName()),
+ () -> assertEquals("TestSecondName", author2.getSecondName())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление автора, который используется в таблице книг")
+ void deleteAuthor() {
+ assertThrows(PersistenceException.class, () -> { authorManageService.deleteAuthor(1L); });
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/app/service/BookManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/BookManageSeviceImplTest.java
new file mode 100644
index 0000000..5dbb053
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/BookManageSeviceImplTest.java
@@ -0,0 +1,116 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 29.05.2019
+ * Time: 13:40
+ */
+
+@DisplayName("Класс BookManageSevice")
+@RunWith(SpringRunner.class)
+@ComponentScan("ru.otus.mkulikov.app")
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class BookManageSeviceImplTest {
+
+ @Autowired
+ private BookManageSevice booksManageSevice;
+
+ @Test
+ @DisplayName("Получение книги по id")
+ void getBookById() {
+ Book book = booksManageSevice.getBookById(1L);
+
+ assertAll(
+ "book",
+ () -> assertNotNull(book),
+ () -> assertNotNull(book.getAuthor()),
+ () -> assertNotNull(book.getGenre()),
+ () -> assertEquals(1L, book.getId()),
+ () -> assertEquals("2019-01-01", DateUtil.dateToString(book.getAddRecordDate())),
+ () -> assertEquals("book_1", book.getCaption()),
+ () -> assertEquals(1, book.getAuthor().getId()),
+ () -> assertEquals(1, book.getGenre().getId()),
+ () -> assertEquals("description", book.getDescription())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех книг")
+ void getBooks() {
+ List books = booksManageSevice.getBooks();
+
+ assertAll(
+ "books",
+ () -> assertNotNull(books),
+ () -> assertEquals(3, books.size()),
+ () -> assertEquals("book_1", books.get(0).getCaption()),
+ () -> assertEquals("book_2", books.get(1).getCaption()),
+ () -> assertEquals("book_3", books.get(2).getCaption())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление книги")
+ void addBook() {
+ int count = booksManageSevice.addBook("Test_Book", 2, 3, "Test_Description");
+ Book book = booksManageSevice.getBookById(4L);
+
+ assertAll(
+ "book",
+ () -> assertNotNull(book),
+ () -> assertEquals(1, count),
+ () -> assertEquals(4L, book.getId()),
+ () -> assertEquals("Test_Book", book.getCaption()),
+ () -> assertEquals(2, book.getAuthor().getId()),
+ () -> assertEquals(3, book.getGenre().getId()),
+ () -> assertEquals("Test_Description", book.getDescription())
+ );
+ }
+
+ @Test
+ @DisplayName("Обновление книги")
+ void updateBook() {
+ Book book1 = booksManageSevice.getBookById(1L);
+ int count = booksManageSevice.updateBook(1L, "Test_Book", 2, 3, "Test_Description");
+ Book book2 = booksManageSevice.getBookById(1L);
+
+ assertAll(
+ "book",
+ () -> assertEquals(1, count),
+ () -> assertEquals("book_1", book1.getCaption()),
+ () -> assertEquals("description", book1.getDescription()),
+ () -> assertEquals("Test_Book", book2.getCaption()),
+ () -> assertEquals("Test_Description", book2.getDescription())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление книги по id")
+ void deleteBook() {
+ int count = booksManageSevice.deleteBook(1L);
+
+ assertAll(
+ "book",
+ () -> assertEquals(1, count),
+ () -> assertThrows(IndexOutOfBoundsException.class, () -> { booksManageSevice.getBookById(1L); })
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/service/CommentManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/CommentManageSeviceImplTest.java
new file mode 100644
index 0000000..e641616
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/CommentManageSeviceImplTest.java
@@ -0,0 +1,126 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:56
+ */
+
+@DisplayName("Класс CommentManageSevice")
+@RunWith(SpringRunner.class)
+@ComponentScan("ru.otus.mkulikov.app")
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class CommentManageSeviceImplTest {
+
+ @Autowired
+ private CommentManageService commentManageService;
+
+ @Test
+ @DisplayName("Получение комментария по id")
+ void getCommentById() {
+ Comment comment = commentManageService.getCommentById(1L);
+
+ assertAll(
+ "comment",
+ () -> assertNotNull(comment),
+ () -> assertNotNull(comment.getBook()),
+ () -> assertEquals(1L, comment.getId()),
+ () -> assertEquals("user1", comment.getUserName()),
+ () -> assertEquals("2019-01-01 10:01:01", DateUtil.dateTimeToString(comment.getAddRecordDate())),
+ () -> assertEquals("text1", comment.getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех комментариев")
+ void getComments() {
+ List comments = commentManageService.getComments();
+
+ assertAll(
+ "comments",
+ () -> assertNotNull(comments),
+ () -> assertEquals(4, comments.size()),
+ () -> assertEquals("text1", comments.get(0).getText()),
+ () -> assertEquals("text2", comments.get(1).getText()),
+ () -> assertEquals("text3", comments.get(2).getText()),
+ () -> assertEquals("text4", comments.get(3).getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление комментария")
+ void addComment() {
+ int count = commentManageService.addComment(1L, "user5", "text5");
+
+ Comment comment = commentManageService.getCommentById(5L);
+
+ assertAll(
+ "comment",
+ () -> assertNotNull(comment),
+ () -> assertNotNull(comment.getBook()),
+ () -> assertEquals(1, count),
+ () -> assertEquals(5L, comment.getId()),
+ () -> assertEquals("user5", comment.getUserName()),
+ () -> assertEquals("text5", comment.getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение комментариев по Id книги")
+ void getCommentsByBookId() {
+ List comments = commentManageService.getCommentsByBookId(1L);
+
+ assertAll(
+ "comments",
+ () -> assertNotNull(comments),
+ () -> assertEquals(2, comments.size()),
+ () -> assertEquals("text1", comments.get(0).getText()),
+ () -> assertEquals("text2", comments.get(1).getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Обновление комментария")
+ void updateComment() {
+ Comment comment1 = commentManageService.getCommentById(1L);
+ int count = commentManageService.updateComment(1L, "TestUser", "TestText");
+ Comment comment2 = commentManageService.getCommentById(1L);
+
+ assertAll(
+ "comment",
+ () -> assertNotNull(comment1),
+ () -> assertNotNull(comment2),
+ () -> assertNotNull(comment1.getBook()),
+ () -> assertNotNull(comment2.getBook()),
+ () -> assertEquals(1, count),
+ () -> assertEquals("user1", comment1.getUserName()),
+ () -> assertEquals("text1", comment1.getText()),
+ () -> assertEquals("TestUser", comment2.getUserName()),
+ () -> assertEquals("TestText", comment2.getText())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление комментария")
+ void deleteComment() {
+ commentManageService.deleteComment(1L);
+ assertNull(commentManageService.getCommentById(1L));
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/app/service/GenreManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/GenreManageSeviceImplTest.java
new file mode 100644
index 0000000..da4d95f
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/GenreManageSeviceImplTest.java
@@ -0,0 +1,98 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import ru.otus.mkulikov.app.model.Genre;
+
+import javax.persistence.PersistenceException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:56
+ */
+
+@DisplayName("Класс GenreManageSevice")
+@RunWith(SpringRunner.class)
+@ComponentScan("ru.otus.mkulikov.app")
+@DataJpaTest
+@TestPropertySource(locations= "classpath:application.yml")
+class GenreManageSeviceImplTest {
+
+ @Autowired
+ private GenreManageService genreManageService;
+
+ @Test
+ @DisplayName("Получение жанра по id")
+ void getGenreById() {
+ Genre genre = genreManageService.getGenreById(1L);
+
+ assertAll(
+ "genre",
+ () -> assertNotNull(genre),
+ () -> assertEquals(1L, genre.getId()),
+ () -> assertEquals("Genre1", genre.getName())
+ );
+ }
+
+ @Test
+ @DisplayName("Получение всех жанров")
+ void getGenres() {
+ List genres = genreManageService.getGenres();
+
+ assertAll(
+ "genres",
+ () -> assertNotNull(genres),
+ () -> assertEquals(3, genres.size()),
+ () -> assertEquals("Genre1", genres.get(0).getName()),
+ () -> assertEquals("Genre2", genres.get(1).getName()),
+ () -> assertEquals("Genre3", genres.get(2).getName())
+ );
+ }
+
+ @Test
+ @DisplayName("Добавление жанра")
+ void addGenre() {
+ int count = genreManageService.addGenre("Test4");
+ Genre genre = genreManageService.getGenreById(4L);
+
+ assertAll(
+ "genre",
+ () -> assertNotNull(genre),
+ () -> assertEquals(1, count),
+ () -> assertEquals(4L, genre.getId()),
+ () -> assertEquals("Test4", genre.getName())
+ );
+ }
+
+ @Test
+ @DisplayName("Обновление жанра")
+ void updateGenre() {
+ Genre genre1 = genreManageService.getGenreById(1L);
+ int count = genreManageService.updateGenre(1L, "UpdatedName");
+ Genre genre2 = genreManageService.getGenreById(1L);
+
+ assertAll(
+ "genre",
+ () -> assertEquals(1, count),
+ () -> assertEquals("Genre1", genre1.getName()),
+ () -> assertEquals("UpdatedName", genre2.getName())
+ );
+ }
+
+ @Test
+ @DisplayName("Удаление жанра, который используется в таблице книг")
+ void deleteGenre() {
+ assertThrows(PersistenceException.class, () -> { genreManageService.deleteGenre(1L); });
+ }
+}
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
new file mode 100644
index 0000000..6175668
--- /dev/null
+++ b/src/test/resources/application.yml
@@ -0,0 +1,17 @@
+spring:
+ banner:
+ location: "classpath:banner-test.txt"
+
+ datasource:
+ initialization-mode: always
+ platform: test
+
+ jpa:
+ hibernate:
+ ddl-auto: none
+
+ properties:
+ hibernate:
+ show_sql: true
+ format_sql: true
+
diff --git a/src/test/resources/banner-test.txt b/src/test/resources/banner-test.txt
new file mode 100644
index 0000000..50a692c
--- /dev/null
+++ b/src/test/resources/banner-test.txt
@@ -0,0 +1,11 @@
+ ___ ___ ___ ___ ___ ___ ___
+ /\__\ /\ \ /\__\ /\ \ /\ \ ___ / /\ / /\ ___
+ /:/ / /::\ \ /:/ / /::\ \ /::\ \ /__/\ / /::\ / /::\ /__/\
+ /:/ / /:/\:\ \ /:/ / /:/\:\ \ /:/\ \ \ \ \:\ / /:/\:\ /__/:/\:\ \ \:\
+ /:/ / ___ /:/ \:\ \ /:/ / /::\~\:\ \ _\:\~\ \ \ \__\:\ / /::\ \:\ _\_ \:\ \:\ \__\:\
+ /:/__/ /\__\ /:/__/ \:\__\ /:/__/ /:/\:\ \:\__\ /\ \:\ \ \__\ *** / /::\ /__/:/\:\ \:\ /__/\ \:\ \:\ / /::\
+ \:\ \ /:/ / \:\ \ /:/ / \:\ \ \:\~\:\ \/__/ \:\ \:\ \/__/ / /:/\:\ \ \:\ \:\_\/ \ \:\ \:\_\/ / /:/\:\
+ \:\ /:/ / \:\ /:/ / \:\ \ \:\ \:\__\ \:\ \:\__\ / /:/__\/ \ \:\ \:\ \ \:\_\:\ / /:/__\/
+ \:\/:/ / \:\/:/ / \:\ \ \:\ \/__/ \:\/:/ / /__/:/ \ \:\_\/ \ \:\/:/ /__/:/
+ \::/ / \::/ / \:\__\ \:\__\ \::/ / \__\/ \ \:\ \ \::/ \__\/
+ \/__/ \/__/ \/__/ \/__/ \/__/ \__\/ \__\/
diff --git a/src/test/resources/data-test.sql b/src/test/resources/data-test.sql
new file mode 100644
index 0000000..75ebe2d
--- /dev/null
+++ b/src/test/resources/data-test.sql
@@ -0,0 +1,39 @@
+insert into AUTHOR (ID, SURNAME, FIRST_NAME, SECOND_NAME)
+values (sq_author.nextval, 'Surname', 'FirstName', 'SecondName');
+
+insert into AUTHOR (ID, SURNAME, FIRST_NAME, SECOND_NAME)
+values (sq_author.nextval, 'Surname2', 'FirstName2', 'SecondName2');
+
+insert into AUTHOR (ID, SURNAME, FIRST_NAME, SECOND_NAME)
+values (sq_author.nextval, 'Surname3', 'FirstName3', 'SecondName3');
+
+--------------------------
+
+insert into GENRE (ID, NAME) values (sq_genre.nextval, 'Genre1');
+insert into GENRE (ID, NAME) values (sq_genre.nextval, 'Genre2');
+insert into GENRE (ID, NAME) values (sq_genre.nextval, 'Genre3');
+
+--------------------------
+
+insert into BOOK (ID, ADD_RECORD_DATE, CAPTION, AUTHOR_ID, GENRE_ID, DESCRIPTION)
+values (sq_book.nextval, to_date('2019-01-01','YYYY-MM-DD'), 'book_1', 1, 1, 'description');
+
+insert into BOOK (ID, ADD_RECORD_DATE, CAPTION, AUTHOR_ID, GENRE_ID, DESCRIPTION)
+values (sq_book.nextval, to_date('2019-01-01','YYYY-MM-DD'), 'book_2', 1, 1, 'description');
+
+insert into BOOK (ID, ADD_RECORD_DATE, CAPTION, AUTHOR_ID, GENRE_ID, DESCRIPTION)
+values (sq_book.nextval, to_date('2019-01-01','YYYY-MM-DD'), 'book_3', 1, 1, 'description');
+
+--------------------------
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 1, to_date('2019-01-01 10:01:01','YYYY-MM-DD HH24-MI-SS'), 'user1', 'text1');
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 1, to_date('2019-01-01 10:01:02','YYYY-MM-DD HH24-MI-SS'), 'user2', 'text2');
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 2, to_date('2019-01-01 10:01:03','YYYY-MM-DD HH24-MI-SS'), 'user3', 'text3');
+
+insert into COMMENT (ID, BOOK_ID, ADD_RECORD_DATE, USER_NAME, TEXT)
+values (sq_comment.nextval, 2, to_date('2019-01-01 10:01:04','YYYY-MM-DD HH24-MI-SS'), 'user4', 'text4');
\ No newline at end of file
diff --git a/src/test/resources/schema-test.sql b/src/test/resources/schema-test.sql
new file mode 100644
index 0000000..7156478
--- /dev/null
+++ b/src/test/resources/schema-test.sql
@@ -0,0 +1,43 @@
+DROP TABLE IF EXISTS BOOK;
+DROP TABLE IF EXISTS AUTHOR;
+DROP TABLE IF EXISTS GENRE;
+DROP TABLE IF EXISTS COMMENT;
+
+CREATE TABLE AUTHOR(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ SURNAME VARCHAR(100) not null,
+ FIRST_NAME VARCHAR(100) not null,
+ SECOND_NAME VARCHAR(100)
+);
+
+CREATE TABLE GENRE(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ NAME VARCHAR(100) not null
+);
+
+CREATE TABLE BOOK(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ ADD_RECORD_DATE DATE default sysdate,
+ CAPTION VARCHAR(255) not null,
+ AUTHOR_ID NUMBER(20,0) not null,
+ GENRE_ID NUMBER(20,0) not null,
+ DESCRIPTION VARCHAR(255),
+
+ foreign key (AUTHOR_ID) references AUTHOR(ID),
+ foreign key (GENRE_ID) references GENRE(ID)
+);
+
+CREATE TABLE COMMENT(
+ ID NUMBER(20,0) PRIMARY KEY not null,
+ BOOK_ID NUMBER(20,0) not null,
+ ADD_RECORD_DATE TIMESTAMP default sysdate,
+ USER_NAME VARCHAR(20),
+ TEXT VARCHAR(500),
+
+ foreign key (BOOK_ID) references BOOK(ID) ON DELETE CASCADE
+);
+
+CREATE SEQUENCE sq_author minvalue 1 start with 1 increment by 1;
+CREATE SEQUENCE sq_genre minvalue 1 start with 1 increment by 1;
+CREATE SEQUENCE sq_book minvalue 1 start with 1 increment by 1;
+CREATE SEQUENCE sq_comment minvalue 1 start with 1 increment by 1;
\ No newline at end of file