diff --git a/core/LvivTest/.gitignore b/core/LvivTest/.gitignore new file mode 100644 index 0000000..90fb021 --- /dev/null +++ b/core/LvivTest/.gitignore @@ -0,0 +1,27 @@ +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ +out/ +data/ \ No newline at end of file diff --git a/core/LvivTest/build.gradle b/core/LvivTest/build.gradle new file mode 100644 index 0000000..e88e244 --- /dev/null +++ b/core/LvivTest/build.gradle @@ -0,0 +1,37 @@ +buildscript { + ext { + springBootVersion = '1.5.7.RELEASE' + } + repositories { + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'org.springframework.boot' + +group = 'edu.cherkassy.test.lviv' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + + +dependencies { + compile('org.springframework.boot:spring-boot-starter-data-elasticsearch') + compile('org.springframework.boot:spring-boot-starter-thymeleaf') + compile('org.springframework.boot:spring-boot-starter-jdbc'){ + exclude (module: "org.apache.tomcat:tomcat-jdbc") + } + runtime('org.springframework.boot:spring-boot-devtools') + runtime('com.h2database:h2') + compileOnly('org.projectlombok:lombok') + testCompile('org.springframework.boot:spring-boot-starter-test') + +} diff --git a/core/LvivTest/gradle/wrapper/gradle-wrapper.jar b/core/LvivTest/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..736fb7d Binary files /dev/null and b/core/LvivTest/gradle/wrapper/gradle-wrapper.jar differ diff --git a/core/LvivTest/gradle/wrapper/gradle-wrapper.properties b/core/LvivTest/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..6109d5b --- /dev/null +++ b/core/LvivTest/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-bin.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/core/LvivTest/gradlew b/core/LvivTest/gradlew new file mode 100644 index 0000000..4453cce --- /dev/null +++ b/core/LvivTest/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/core/LvivTest/gradlew.bat b/core/LvivTest/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/core/LvivTest/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/TestLvivApplication.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/TestLvivApplication.java new file mode 100644 index 0000000..ab5082c --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/TestLvivApplication.java @@ -0,0 +1,24 @@ +package edu.cherkassy.test.lviv.testlviv; + +import edu.cherkassy.test.lviv.testlviv.service.BookService; +import edu.cherkassy.test.lviv.testlviv.service.impls.BookServiceImpl; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TestLvivApplication { + public static void main(String[] args) { + BookService repository = SpringApplication + .run(TestLvivApplication.class, args) + .getBean(BookServiceImpl.class); + + if (repository != null) { + System.out.println("\n*****TEST getAllBooks() method: ******"); + repository.getAllBooks().forEach(System.out::println); + + System.out.println("\n*****TEST getBookById() method: *****"); + System.out.println(repository.getBookById(2)); + System.out.println(repository.getBookById(1)); + } + } +} diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/controllers/BookController.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/controllers/BookController.java new file mode 100644 index 0000000..6e91faf --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/controllers/BookController.java @@ -0,0 +1,44 @@ +package edu.cherkassy.test.lviv.testlviv.controllers; + +import edu.cherkassy.test.lviv.testlviv.entity.Book; +import edu.cherkassy.test.lviv.testlviv.entity.Isbn; +import edu.cherkassy.test.lviv.testlviv.service.BookService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/lviv/books") +public class BookController { + + private BookService bookService; + + @Autowired + public BookController(BookService bookService) { + this.bookService = bookService; + } + + @GetMapping() + public List getAllBooks() { + return this.bookService.getAllBooks(); + } + + @GetMapping("/{bookId}") + public Book getBookById(@PathVariable Integer bookId) { + return this.bookService.getBookById(bookId); + } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public void addNewBook(@RequestBody Book book) { + this.bookService.addNewBook(book); + } + + @PostMapping("/isbn") + @ResponseStatus(HttpStatus.CREATED) + public void addNewIsbn(@RequestBody Isbn isbn) { + this.bookService.addNewIsbn(isbn); + } +} \ No newline at end of file diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/entity/Book.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/entity/Book.java new file mode 100644 index 0000000..d4fabbc --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/entity/Book.java @@ -0,0 +1,35 @@ +package edu.cherkassy.test.lviv.testlviv.entity; + +import lombok.*; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Book { + private Integer id; + private String title; + private List authors = new ArrayList<>(); + private String publishing; + private String yearOfPublishing; + private Short numberOfPages; + private List isbnList = new ArrayList<>(); + private List categories = new ArrayList<>(); + private String description; + private Double price; + + public static Book resultSetExtractor(ResultSet rs, Book book) throws SQLException { + book.setId(rs.getInt("BOOKID")); + book.setTitle(rs.getString("TITLE")); + book.setPublishing(rs.getString("PUBLISHING")); + book.setYearOfPublishing(rs.getString("YEAROFPUBLISHING")); + book.setNumberOfPages(rs.getShort("NUMBEROFPAGE")); + book.setPrice(rs.getDouble("PRICE")); + book.setDescription(rs.getString("DESCRIPTION")); + return book; + } +} \ No newline at end of file diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/entity/Isbn.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/entity/Isbn.java new file mode 100644 index 0000000..9cb30aa --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/entity/Isbn.java @@ -0,0 +1,28 @@ +package edu.cherkassy.test.lviv.testlviv.entity; + +import lombok.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Isbn { + private Integer isbnId; + private Integer bookId; + private String language; + private String number; + private String type; + private Boolean translation; + + public static Isbn resultSetExtractor(ResultSet rs, Isbn isbn) throws SQLException { + isbn.setIsbnId(rs.getInt("ISBNID")); + isbn.setBookId(rs.getInt("BOOKID")); + isbn.setNumber(rs.getString("NUMBER")); + isbn.setType(rs.getString("TYPE")); + isbn.setTranslation(rs.getBoolean("TRANSLATION")); + isbn.setLanguage(rs.getString("LANGUAGE")); + return isbn; + } +} \ No newline at end of file diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/repository/BookRepository.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/repository/BookRepository.java new file mode 100644 index 0000000..519fb29 --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/repository/BookRepository.java @@ -0,0 +1,13 @@ +package edu.cherkassy.test.lviv.testlviv.repository; + +import edu.cherkassy.test.lviv.testlviv.entity.Book; +import edu.cherkassy.test.lviv.testlviv.entity.Isbn; + +import java.util.List; + +public interface BookRepository { + List getAllBooks(); + Book getBookById(Integer bookId); + int addNewIsbn(Isbn isbn); + int addNewBook(Book book); +} diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/repository/impls/BookRepositoryH2DbImpl.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/repository/impls/BookRepositoryH2DbImpl.java new file mode 100644 index 0000000..30a3f19 --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/repository/impls/BookRepositoryH2DbImpl.java @@ -0,0 +1,215 @@ +package edu.cherkassy.test.lviv.testlviv.repository.impls; + +import edu.cherkassy.test.lviv.testlviv.entity.Book; +import edu.cherkassy.test.lviv.testlviv.entity.Isbn; +import edu.cherkassy.test.lviv.testlviv.repository.BookRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementCallback; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.*; + +@Repository +public class BookRepositoryH2DbImpl implements BookRepository { + + private static final String SQL_SELECT_BOOKS = "SELECT " + + "LVIV.BOOKS.BOOKID, " + + "LVIV.BOOKS.TITLE, " + + "LVIV.BOOKS.PUBLISHING, " + + "LVIV.BOOKS.YEAROFPUBLISHING, " + + "LVIV.BOOKS.NUMBEROFPAGE, " + + "LVIV.BOOKS.PRICE, " + + "LVIV.BOOKS.DESCRIPTION " + + " FROM LVIV.BOOKS"; + + private static final String SQL_SELECT_ISBN = "SELECT" + + " LVIV.ISBN.ISBNID," + + " LVIV.ISBN.BOOKID," + + " LVIV.ISBN.NUMBER," + + " LVIV.ISBN.TYPE," + + " LVIV.ISBN.TRANSLATION," + + " LVIV.ISBN.LANGUAGE" + + " FROM LVIV.ISBN WHERE BOOKID = ?"; + + private static final String SQL_SELECT_CATEGORIES = "SELECT LVIV.CATEGORY.CATEGORY " + + "FROM LVIV.CATEGORY" + + " INNER JOIN LVIV.BOOK_CATEGORY ON LVIV.BOOK_CATEGORY.CATEGORYID = LVIV.CATEGORY.CATEGORYID" + + " INNER JOIN LVIV.BOOKS ON LVIV.BOOK_CATEGORY.BOOKID = LVIV.BOOKS.BOOKID " + + "WHERE LVIV.BOOKS.BOOKID = ?"; + + private static final String SQL_SELECT_AUTHORS = "SELECT LVIV.AUTHORS.NAME " + + "FROM LVIV.AUTHORS" + + " INNER JOIN LVIV.AUTHOR_BOOK ON LVIV.AUTHOR_BOOK.AUTHORID = LVIV.AUTHORS.AUTHORID" + + " INNER JOIN LVIV.BOOKS ON LVIV.AUTHOR_BOOK.BOOKID = LVIV.BOOKS.BOOKID " + + "WHERE LVIV.BOOKS.BOOKID = ?"; + + private static final String SQL_SELECT_BOOK_BY_ID = SQL_SELECT_BOOKS + " WHERE LVIV.BOOKS.BOOKID = ?"; + + private static final String SQL_INSERT_BOOK = "INSERT INTO LVIV.BOOKS " + + "(BOOKID ,TITLE, PUBLISHING, YEAROFPUBLISHING, NUMBEROFPAGE, PRICE, DESCRIPTION) " + + "VALUES (DEFAULT ,?, ?, ?, ?, ?, ?)"; + + private static final String SQL_INSERT_ISBN = "INSERT INTO LVIV.ISBN " + + "(ISBNID, BOOKID, LANGUAGE, NUMBER, TYPE, TRANSLATION) " + + "VALUES (DEFAULT, ?, ?, ?, ?, ?)"; + + private static final String SQL_INSERT_CATEGORY = "INSERT INTO LVIV.CATEGORY " + + " (CATEGORYID, CATEGORY) VALUES (DEFAULT , ?)"; + + private static final String SQL_INSERT_AUTHORS = "INSERT INTO LVIV.AUTHORS" + + " (AUTHORID, NAME) VALUES (DEFAULT , ?)"; + + private static final String SQL_INSERT_BOOK_CATEGORY = "INSERT INTO LVIV.BOOK_CATEGORY " + + "(BOOKID, CATEGORYID) VALUES (?, ?)"; + + private static final String SQL_INSERT_AUTHOR_BOOK = "INSERT INTO LVIV.AUTHOR_BOOK " + + "(BOOKID, AUTHORID) VALUES (?, ?)"; + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public BookRepositoryH2DbImpl(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public List getAllBooks() { + List books = this.getBooks(); + books.forEach(book -> book.setIsbnList(this.getListIsbnsOfBook(book.getId()))); + books.forEach(book -> book.setCategories(this.getBookStringAttributes(book.getId(), SQL_SELECT_CATEGORIES, "CATEGORY"))); + books.forEach(book -> book.setAuthors(this.getBookStringAttributes(book.getId(), SQL_SELECT_AUTHORS, "NAME"))); + return books; + } + + @Override + public Book getBookById(Integer bookId) { + Book book = this.getBook(bookId); + book.setIsbnList(this.getListIsbnsOfBook(bookId)); + book.setCategories(this.getBookStringAttributes(bookId, SQL_SELECT_CATEGORIES, "CATEGORY")); + book.setAuthors(this.getBookStringAttributes(bookId, SQL_SELECT_AUTHORS, "NAME")); + return book; + } + + @Override + public int addNewIsbn(Isbn isbn) { + return this.addIsbns(Collections.singletonList(isbn)); + } + + @Override + public int addNewBook(Book book) { + int rowAddCount = 0; + + KeyHolder holder = new GeneratedKeyHolder(); + rowAddCount += this.jdbcTemplate.update(con -> { + PreparedStatement ps = con.prepareStatement(SQL_INSERT_BOOK, Statement.RETURN_GENERATED_KEYS); + ps.setString(1, book.getTitle()); + ps.setString(2, book.getPublishing()); + ps.setString(3, book.getYearOfPublishing()); + ps.setShort(4, book.getNumberOfPages()); + ps.setDouble(5, book.getPrice()); + ps.setString(6, book.getDescription()); + return ps; + }, holder); + + Integer generatedId = holder.getKey().intValue(); + book.setId(generatedId); + book.getIsbnList().forEach(isbn -> isbn.setBookId(generatedId)); + + rowAddCount += this.addIsbns(book.getIsbnList()); + HashMap> attrs = new HashMap<>(); + attrs.put("CATEGORIES", book.getCategories()); + attrs.put("NAME", book.getAuthors()); + + rowAddCount += this.addStringsAttributesToBook(attrs, book.getId()); + + return rowAddCount; + } + + private int addIsbns(List isbns) { + int addedRows = 0; + for (Isbn isbn : isbns) { + addedRows += this.jdbcTemplate.update(SQL_INSERT_ISBN, + isbn.getBookId(), + isbn.getLanguage(), + isbn.getNumber(), + isbn.getType(), + isbn.getTranslation()); + } + return addedRows; + } + + private int addStringsAttributesToBook(Map> attributes, Integer bookId) { + int rowAddCount = 0; + for (Map.Entry> entry : attributes.entrySet()) { + if (entry.getKey().equals("CATEGORIES")) { + for (String attribute : entry.getValue()) { + rowAddCount += fillIntermediateTable(attribute, SQL_INSERT_CATEGORY, SQL_INSERT_BOOK_CATEGORY, bookId); + } + } else if (entry.getKey().equals("NAME")) { + for (String attribute : entry.getValue()) { + rowAddCount += fillIntermediateTable(attribute, SQL_INSERT_AUTHORS, SQL_INSERT_AUTHOR_BOOK, bookId); + } + } + } + return rowAddCount; + } + + private int fillIntermediateTable(String attr, String insertAttribute, String insertIntermediateTable, Integer bookId) { + int rowAddCount = 0; + KeyHolder holder = new GeneratedKeyHolder(); + rowAddCount += this.jdbcTemplate.update(con -> { + PreparedStatement ps = con.prepareStatement(insertAttribute, Statement.RETURN_GENERATED_KEYS); + ps.setString(1, attr); + return ps; + }, holder); + Long attrId = holder.getKey().longValue(); + rowAddCount += this.jdbcTemplate.update(insertIntermediateTable, bookId, attrId); + return rowAddCount; + } + + private Book getBook(Integer bookId) { + return this.jdbcTemplate.queryForObject(SQL_SELECT_BOOK_BY_ID, + new Object[]{bookId}, + (rs, rowNum) -> Book.resultSetExtractor(rs, new Book())); + } + + private List getBooks() { + return jdbcTemplate.query(SQL_SELECT_BOOKS, rs -> { + List result = new ArrayList<>(); + while (rs.next()) { + result.add(Book.resultSetExtractor(rs, new Book())); + } + return result; + }); + } + + private List getListIsbnsOfBook(Integer bookId) { + List isbns = new ArrayList<>(); + return this.jdbcTemplate.execute(SQL_SELECT_ISBN, (PreparedStatementCallback>) ps -> { + ps.setInt(1, bookId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + isbns.add(Isbn.resultSetExtractor(rs, new Isbn())); + } + return isbns; + }); + } + + private List getBookStringAttributes(Integer bookId, String query, String titleOfColumn) { + List resultOfQuery = new ArrayList<>(); + return this.jdbcTemplate.execute(query, (PreparedStatementCallback>) ps -> { + ps.setInt(1, bookId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + resultOfQuery.add(rs.getString(titleOfColumn)); + } + return resultOfQuery; + }); + } +} \ No newline at end of file diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/service/BookService.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/service/BookService.java new file mode 100644 index 0000000..2a1cbb1 --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/service/BookService.java @@ -0,0 +1,13 @@ +package edu.cherkassy.test.lviv.testlviv.service; + +import edu.cherkassy.test.lviv.testlviv.entity.Book; +import edu.cherkassy.test.lviv.testlviv.entity.Isbn; + +import java.util.List; + +public interface BookService { + List getAllBooks(); + Book getBookById(Integer bookId); + int addNewIsbn(Isbn isbn); + int addNewBook(Book book); +} diff --git a/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/service/impls/BookServiceImpl.java b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/service/impls/BookServiceImpl.java new file mode 100644 index 0000000..e565b97 --- /dev/null +++ b/core/LvivTest/src/main/java/edu/cherkassy/test/lviv/testlviv/service/impls/BookServiceImpl.java @@ -0,0 +1,42 @@ +package edu.cherkassy.test.lviv.testlviv.service.impls; + +import edu.cherkassy.test.lviv.testlviv.entity.Book; +import edu.cherkassy.test.lviv.testlviv.entity.Isbn; +import edu.cherkassy.test.lviv.testlviv.repository.BookRepository; +import edu.cherkassy.test.lviv.testlviv.service.BookService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class BookServiceImpl implements BookService { + + private BookRepository bookRepository; + + @Autowired + public BookServiceImpl(BookRepository bookRepository) { + this.bookRepository = bookRepository; + } + + + @Override + public List getAllBooks() { + return bookRepository.getAllBooks(); + } + + @Override + public Book getBookById(Integer bookId) { + return bookRepository.getBookById(bookId); + } + + @Override + public int addNewIsbn(Isbn isbn) { + return bookRepository.addNewIsbn(isbn); + } + + @Override + public int addNewBook(Book book) { + return bookRepository.addNewBook(book); + } +} diff --git a/core/LvivTest/src/main/resources/application.properties b/core/LvivTest/src/main/resources/application.properties new file mode 100644 index 0000000..42ee1f4 --- /dev/null +++ b/core/LvivTest/src/main/resources/application.properties @@ -0,0 +1,6 @@ +spring.datasource.url=jdbc:h2:tcp://localhost:9092/LVIV +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=root +spring.datasource.password=1 +spring.h2.console.enabled=true +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration \ No newline at end of file diff --git a/core/LvivTest/src/main/resources/sql/dump.sql b/core/LvivTest/src/main/resources/sql/dump.sql new file mode 100644 index 0000000..2cce21e --- /dev/null +++ b/core/LvivTest/src/main/resources/sql/dump.sql @@ -0,0 +1,86 @@ +; +CREATE USER IF NOT EXISTS "" SALT '' HASH '' ADMIN; +CREATE USER IF NOT EXISTS ROOT SALT 'eed89a9e45cbe153' HASH 'dfeff67a44c88dab302d3e71eb033b27bc51b94f6c81b579c61ede552d507009' ADMIN; +CREATE SCHEMA IF NOT EXISTS LVIV AUTHORIZATION ""; +CREATE SEQUENCE LVIV.SYSTEM_SEQUENCE_4740355A_BFAE_4F1E_B9C6_80DEDD87C244 START WITH 1 BELONGS_TO_TABLE; +CREATE SEQUENCE LVIV.SYSTEM_SEQUENCE_2A08AEDE_51B4_4418_887D_8D84F9F20848 START WITH 3 BELONGS_TO_TABLE; +CREATE SEQUENCE LVIV.SYSTEM_SEQUENCE_7673572B_E411_4639_87AE_55A3D08B9944 START WITH 5 BELONGS_TO_TABLE; +CREATE SEQUENCE LVIV.SYSTEM_SEQUENCE_DE11B891_CF74_4124_AFA7_2C7498078821 START WITH 4 BELONGS_TO_TABLE; +CREATE SEQUENCE LVIV.SYSTEM_SEQUENCE_6978E8A1_ACF5_4DA3_8875_EEBD229642E0 START WITH 5 BELONGS_TO_TABLE; + +CREATE CACHED TABLE LVIV.AUTHOR_BOOK( + AUTHORID INT NOT NULL, + BOOKID INT NOT NULL +); +-- 3 +/- SELECT COUNT(*) FROM LVIV.AUTHOR_BOOK; +INSERT INTO LVIV.AUTHOR_BOOK(AUTHORID, BOOKID) VALUES +(1, 1), +(2, 2), +(3, 2); +CREATE CACHED TABLE LVIV.AUTHORS( + AUTHORID INT DEFAULT (NEXT VALUE FOR LVIV.SYSTEM_SEQUENCE_DE11B891_CF74_4124_AFA7_2C7498078821) NOT NULL NULL_TO_DEFAULT SEQUENCE LVIV.SYSTEM_SEQUENCE_DE11B891_CF74_4124_AFA7_2C7498078821, + NAME VARCHAR(255) NOT NULL +); +ALTER TABLE LVIV.AUTHORS ADD CONSTRAINT LVIV.CONSTRAINT_4 PRIMARY KEY(AUTHORID); +-- 3 +/- SELECT COUNT(*) FROM LVIV.AUTHORS; +INSERT INTO LVIV.AUTHORS(AUTHORID, NAME) VALUES +(1, 'Alan Beaulieu'), +(2, 'Scott Chacon'), +(3, 'Ben Straub'); +CREATE UNIQUE INDEX LVIV.AUTHORS_NAME_UINDEX ON LVIV.AUTHORS(NAME); +CREATE CACHED TABLE LVIV.CATEGORY( + CATEGORYID INT DEFAULT (NEXT VALUE FOR LVIV.SYSTEM_SEQUENCE_7673572B_E411_4639_87AE_55A3D08B9944) NOT NULL NULL_TO_DEFAULT SEQUENCE LVIV.SYSTEM_SEQUENCE_7673572B_E411_4639_87AE_55A3D08B9944, + CATEGORY VARCHAR(255) NOT NULL +); +ALTER TABLE LVIV.CATEGORY ADD CONSTRAINT LVIV.CONSTRAINT_31 PRIMARY KEY(CATEGORYID); +-- 4 +/- SELECT COUNT(*) FROM LVIV.CATEGORY; +INSERT INTO LVIV.CATEGORY(CATEGORYID, CATEGORY) VALUES +(1, 'SQL'), +(2, 'Relational DB'), +(3, 'Git'), +(4, 'VCS'); +CREATE UNIQUE INDEX LVIV.CATEGORY_CATEGORY_UINDEX ON LVIV.CATEGORY(CATEGORY); +CREATE CACHED TABLE LVIV.ISBN( + ISBNID INT DEFAULT (NEXT VALUE FOR LVIV.SYSTEM_SEQUENCE_6978E8A1_ACF5_4DA3_8875_EEBD229642E0) NOT NULL NULL_TO_DEFAULT SEQUENCE LVIV.SYSTEM_SEQUENCE_6978E8A1_ACF5_4DA3_8875_EEBD229642E0, + BOOKID INT NOT NULL, + LANGUAGE CHAR(4) NOT NULL, + NUMBER VARCHAR(13) NOT NULL, + TYPE CHAR(2) NOT NULL, + TRANSLATION BOOLEAN NOT NULL +); +ALTER TABLE LVIV.ISBN ADD CONSTRAINT LVIV.CONSTRAINT_2 PRIMARY KEY(ISBNID); +-- 4 +/- SELECT COUNT(*) FROM LVIV.ISBN; +INSERT INTO LVIV.ISBN(ISBNID, BOOKID, LANGUAGE, NUMBER, TYPE, TRANSLATION) VALUES +(1, 1, 'UA', '9785496017633', '13', TRUE), +(2, 1, 'EN', '9781484200773', '13', FALSE), +(3, 2, 'RU', '9785496017633', '13', TRUE), +(4, 2, 'EN', '9781484200733', '13', FALSE); +CREATE CACHED TABLE LVIV.BOOK_CATEGORY( + BOOKID INT NOT NULL, + CATEGORYID INT NOT NULL +); +-- 4 +/- SELECT COUNT(*) FROM LVIV.BOOK_CATEGORY; +INSERT INTO LVIV.BOOK_CATEGORY(BOOKID, CATEGORYID) VALUES +(1, 1), +(1, 2), +(2, 3), +(2, 4); +CREATE CACHED TABLE LVIV.BOOKS( + BOOKID INT DEFAULT (NEXT VALUE FOR LVIV.SYSTEM_SEQUENCE_2A08AEDE_51B4_4418_887D_8D84F9F20848) NOT NULL NULL_TO_DEFAULT SEQUENCE LVIV.SYSTEM_SEQUENCE_2A08AEDE_51B4_4418_887D_8D84F9F20848, + TITLE VARCHAR(255) NOT NULL, + PUBLISHING VARCHAR(255) NOT NULL, + YEAROFPUBLISHING VARCHAR(4) NOT NULL, + NUMBEROFPAGE SMALLINT NOT NULL, + PRICE DOUBLE NOT NULL, + DESCRIPTION TEXT +); +ALTER TABLE LVIV.BOOKS ADD CONSTRAINT LVIV.CONSTRAINT_3 PRIMARY KEY(BOOKID); +-- 2 +/- SELECT COUNT(*) FROM LVIV.BOOKS; +INSERT INTO LVIV.BOOKS(BOOKID, TITLE, PUBLISHING, YEAROFPUBLISHING, NUMBEROFPAGE, PRICE, DESCRIPTION) VALUES +(1, 'Learn SQL', 'O''Relly', '2007', 310, 100.0, STRINGDECODE('\u041a\u043d\u0438\u0433\u0430 \u0410\u043b\u0430\u043d\u0430 \u0411\u044c\u044e\u043b\u0438, \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u0430 \u043f\u043e \u044f\u0437\u044b\u043a\u0443 SQL, - \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u044b\u0439 \u0443\u0447\u0435\u0431\u043d\u0438\u043a \u0434\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u0435\u0449\u0435 \u043d\u0435 \u0437\u043d\u0430\u0435\u0442, \u043d\u043e \u0445\u043e\u0447\u0435\u0442 \u043e\u0441\u0432\u043e\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u044f\u0437\u044b\u043a. \u041a\u043d\u0438\u0433\u0430 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043f\u0440\u0438\u043e\u0431\u0440\u0435\u0441\u0442\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f, \u043d\u043e \u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u043e \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0442\u043e \u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u043c\u044b\u0445 \u043c\u043e\u0449\u043d\u044b\u0445 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430\u0445 \u044f\u0437\u044b\u043a\u0430 SQL, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u043e\u043f\u044b\u0442\u043d\u044b\u043c\u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c\u0438.')), +(2, STRINGDECODE('Git \u0434\u043b\u044f \u043f\u0440\u043e\u0444\u0435\u0441\u0441\u0438\u043e\u043d\u0430\u043b\u044c\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430'), STRINGDECODE('\u041f\u0438\u0442\u0435\u0440'), '2016', 496, 212.45, STRINGDECODE('\u042d\u0442\u0430 \u043a\u043d\u0438\u0433\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e Git \u0432 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445. \u0421 \u0442\u0435\u0445 \u043f\u043e\u0440 \u043a\u0430\u043a \u043f\u0440\u043e\u0435\u043a\u0442 Git \u2014 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438 \u2014 \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d \u041b\u0438\u043d\u0443\u0441\u043e\u043c \u0422\u043e\u0440\u0432\u0430\u043b\u044c\u0434\u0441\u043e\u043c, \u043f\u0440\u043e\u0448\u043b\u043e \u043c\u043d\u043e\u0433\u043e \u043b\u0435\u0442, \u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 Git \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043b\u0430\u0441\u044c \u0432 \u0434\u043e\u043c\u0438\u043d\u0438\u0440\u0443\u044e\u0449\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0432\u0435\u0440\u0441\u0438\u0439, \u043a\u0430\u043a \u0434\u043b\u044f \u043a\u043e\u043c\u043c\u0435\u0440\u0447\u0435\u0441\u043a\u0438\u0445 \u0446\u0435\u043b\u0435\u0439, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c')); +ALTER TABLE LVIV.AUTHOR_BOOK ADD CONSTRAINT LVIV.AUTHOR_BOOK_BOOKS_BOOKID_FK FOREIGN KEY(BOOKID) REFERENCES LVIV.BOOKS(BOOKID) NOCHECK; +ALTER TABLE LVIV.BOOK_CATEGORY ADD CONSTRAINT LVIV.BOOK_CATEGORY_CATEGORY_CATEGORYID_FK FOREIGN KEY(CATEGORYID) REFERENCES LVIV.CATEGORY(CATEGORYID) NOCHECK; +ALTER TABLE LVIV.BOOK_CATEGORY ADD CONSTRAINT LVIV.BOOK_CATEGORY_BOOKS_BOOKID_FK FOREIGN KEY(BOOKID) REFERENCES LVIV.BOOKS(BOOKID) NOCHECK; +ALTER TABLE LVIV.AUTHOR_BOOK ADD CONSTRAINT LVIV.AUTHOR_BOOK_AUTHORS_AUTHORID_FK FOREIGN KEY(AUTHORID) REFERENCES LVIV.AUTHORS(AUTHORID) NOCHECK; +ALTER TABLE LVIV.ISBN ADD CONSTRAINT LVIV.ISBN_BOOKS_BOOKID_FK FOREIGN KEY(BOOKID) REFERENCES LVIV.BOOKS(BOOKID) NOCHECK; diff --git a/core/LvivTest/src/test/java/edu/cherkassy/test/lviv/testlviv/TestLvivApplicationTests.java b/core/LvivTest/src/test/java/edu/cherkassy/test/lviv/testlviv/TestLvivApplicationTests.java new file mode 100644 index 0000000..775f019 --- /dev/null +++ b/core/LvivTest/src/test/java/edu/cherkassy/test/lviv/testlviv/TestLvivApplicationTests.java @@ -0,0 +1,16 @@ +package edu.cherkassy.test.lviv.testlviv; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class TestLvivApplicationTests { + + @Test + public void contextLoads() { + } + +}