diff --git a/SpringApp/library/build.gradle b/SpringApp/library/build.gradle index cd71bb9..6cb9492 100644 --- a/SpringApp/library/build.gradle +++ b/SpringApp/library/build.gradle @@ -7,6 +7,17 @@ plugins { group = 'com.ip' version = '0.0.1-SNAPSHOT' +defaultTasks 'bootRun' + +jar { + enabled = false +} + +bootJar { + archiveFileName = String.format('%s-%s.jar', rootProject.name, version) +} + +assert System.properties['java.specification.version'] == '17' || '21' java { sourceCompatibility = '17' } @@ -17,10 +28,12 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' implementation 'org.modelmapper:modelmapper:3.2.0' - - developmentOnly 'org.springframework.boot:spring-boot-devtools' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'com.h2database:h2:2.2.224' testImplementation 'org.springframework.boot:spring-boot-starter-test' } diff --git a/SpringApp/library/src/main/java/com/ip/library/authors/api/AuthorDto.java b/SpringApp/library/src/main/java/com/ip/library/authors/api/AuthorDto.java index 4601838..59fb8c3 100644 --- a/SpringApp/library/src/main/java/com/ip/library/authors/api/AuthorDto.java +++ b/SpringApp/library/src/main/java/com/ip/library/authors/api/AuthorDto.java @@ -3,13 +3,15 @@ package com.ip.library.authors.api; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; public class AuthorDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Long id; @NotBlank + @Size(min = 5, max = 20) private String name; - @JsonProperty(access = JsonProperty.Access.READ_ONLY) public Long getId() { return id; } diff --git a/SpringApp/library/src/main/java/com/ip/library/authors/model/AuthorEntity.java b/SpringApp/library/src/main/java/com/ip/library/authors/model/AuthorEntity.java index 9f6df0a..2420b69 100644 --- a/SpringApp/library/src/main/java/com/ip/library/authors/model/AuthorEntity.java +++ b/SpringApp/library/src/main/java/com/ip/library/authors/model/AuthorEntity.java @@ -4,15 +4,21 @@ import java.util.Objects; import com.ip.library.core.model.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "authors") public class AuthorEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 20) private String name; public AuthorEntity() { super(); } - public AuthorEntity(Long id, String name) { - super(id); + public AuthorEntity(String name) { this.name = name; } diff --git a/SpringApp/library/src/main/java/com/ip/library/authors/repository/AuthorRepository.java b/SpringApp/library/src/main/java/com/ip/library/authors/repository/AuthorRepository.java index f8c04fd..0e8e9b6 100644 --- a/SpringApp/library/src/main/java/com/ip/library/authors/repository/AuthorRepository.java +++ b/SpringApp/library/src/main/java/com/ip/library/authors/repository/AuthorRepository.java @@ -1,10 +1,11 @@ package com.ip.library.authors.repository; -import org.springframework.stereotype.Repository; +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; import com.ip.library.authors.model.AuthorEntity; -import com.ip.library.core.repository.MapRepository; -@Repository -public class AuthorRepository extends MapRepository { +public interface AuthorRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); } diff --git a/SpringApp/library/src/main/java/com/ip/library/authors/service/AuthorService.java b/SpringApp/library/src/main/java/com/ip/library/authors/service/AuthorService.java index cc616d3..800ff31 100644 --- a/SpringApp/library/src/main/java/com/ip/library/authors/service/AuthorService.java +++ b/SpringApp/library/src/main/java/com/ip/library/authors/service/AuthorService.java @@ -2,8 +2,10 @@ package com.ip.library.authors.service; import java.util.List; import java.util.Optional; +import java.util.stream.StreamSupport; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.ip.library.core.error.NotFoundException; import com.ip.library.authors.model.AuthorEntity; @@ -17,27 +19,49 @@ public class AuthorService { this.repository = repository; } + private void checkNameUniqueness(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Author with name %s already exists", name) + ); + } + } + + @Transactional(readOnly = true) public List getAll() { - return repository.getAll(); + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); } + @Transactional(readOnly = true) public AuthorEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(AuthorEntity.class, id)); } + @Transactional public AuthorEntity create(AuthorEntity entity) { - return repository.create(entity); + if (entity == null) { + throw new IllegalArgumentException("Creating AuthorEntity is null"); + } + checkNameUniqueness(entity.getName()); + return repository.save(entity); } + @Transactional public AuthorEntity update(Long id, AuthorEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Updating AuthorEntity is null"); + } final AuthorEntity existsEntity = get(id); + checkNameUniqueness(entity.getName()); existsEntity.setName(entity.getName()); - return repository.update(existsEntity); + return repository.save(existsEntity); } + @Transactional public AuthorEntity delete(Long id) { final AuthorEntity existsEntity = get(id); - return repository.delete(existsEntity); + repository.delete(existsEntity); + return existsEntity; } } diff --git a/SpringApp/library/src/main/java/com/ip/library/books/model/BookEntity.java b/SpringApp/library/src/main/java/com/ip/library/books/model/BookEntity.java index e208a76..85831e7 100644 --- a/SpringApp/library/src/main/java/com/ip/library/books/model/BookEntity.java +++ b/SpringApp/library/src/main/java/com/ip/library/books/model/BookEntity.java @@ -4,19 +4,32 @@ import java.util.Objects; import com.ip.library.core.model.BaseEntity; import com.ip.library.types.model.TypeEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + import com.ip.library.authors.model.AuthorEntity; +@Entity +@Table(name = "books") public class BookEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) private String name; + @ManyToOne + @JoinColumn(name = "typeId", nullable = false) private TypeEntity type; + @ManyToOne + @JoinColumn(name = "authorId", nullable = false) private AuthorEntity author; public BookEntity() { super(); } - public BookEntity(Long id, String name, TypeEntity type, AuthorEntity author) { - super(id); + public BookEntity(String name, TypeEntity type, AuthorEntity author) { this.name = name; this.type = type; this.author = author; diff --git a/SpringApp/library/src/main/java/com/ip/library/books/repository/BookRepository.java b/SpringApp/library/src/main/java/com/ip/library/books/repository/BookRepository.java index 55e82c7..ef63859 100644 --- a/SpringApp/library/src/main/java/com/ip/library/books/repository/BookRepository.java +++ b/SpringApp/library/src/main/java/com/ip/library/books/repository/BookRepository.java @@ -1,10 +1,15 @@ package com.ip.library.books.repository; -import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; import com.ip.library.books.model.BookEntity; -import com.ip.library.core.repository.MapRepository; -@Repository -public class BookRepository extends MapRepository { +public interface BookRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); + List findByTypeId(long typeId); + List findByAuthorId(long authorId); + List findByTypeIdAndAuthorId(long typeId, long authorId); } diff --git a/SpringApp/library/src/main/java/com/ip/library/books/service/BookService.java b/SpringApp/library/src/main/java/com/ip/library/books/service/BookService.java index 6828d25..5aa71b9 100644 --- a/SpringApp/library/src/main/java/com/ip/library/books/service/BookService.java +++ b/SpringApp/library/src/main/java/com/ip/library/books/service/BookService.java @@ -3,8 +3,10 @@ package com.ip.library.books.service; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.StreamSupport; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.ip.library.books.model.BookEntity; import com.ip.library.books.repository.BookRepository; @@ -18,34 +20,37 @@ public class BookService { this.repository = repository; } + @Transactional(readOnly = true) public List getAll(Long typeId, Long authorId) { - List result = repository.getAll(); - if (!Objects.equals(typeId, 0L)){ - result = result.stream() - .filter(item -> item.getType().getId().equals(typeId)) - .toList(); + if (typeId <= 0L && authorId <= 0L) { + return StreamSupport.stream(repository.findAll().spliterator(), null).toList(); } - if (!Objects.equals(authorId, 0L)){ - result = result.stream() - .filter(item -> item.getAuthor().getId().equals(authorId)) - .toList(); + if (typeId <= 0L){ + return repository.findByAuthorId(authorId); } - return result; + if (authorId <= 0L){ + return repository.findByTypeId(typeId); + } + return repository.findByTypeIdAndAuthorId(typeId, authorId); } + @Transactional(readOnly = true) public List getAll() { return repository.getAll(); } + @Transactional(readOnly = true) public BookEntity get(Long id) { return Optional.ofNullable(repository.get(id)) .orElseThrow(() -> new NotFoundException(id)); } + @Transactional public BookEntity create(BookEntity entity) { return repository.create(entity); } + @Transactional public BookEntity update(Long id, BookEntity entity) { final BookEntity existsEntity = get(id); existsEntity.setName(entity.getName()); @@ -54,6 +59,7 @@ public class BookService { return repository.update(existsEntity); } + @Transactional public BookEntity delete(Long id) { final BookEntity existsEntity = get(id); return repository.delete(existsEntity); diff --git a/SpringApp/library/src/main/java/com/ip/library/core/configuration/Constants.java b/SpringApp/library/src/main/java/com/ip/library/core/configuration/Constants.java index 4e40f87..198d3a6 100644 --- a/SpringApp/library/src/main/java/com/ip/library/core/configuration/Constants.java +++ b/SpringApp/library/src/main/java/com/ip/library/core/configuration/Constants.java @@ -1,6 +1,8 @@ package com.ip.library.core.configuration; public class Constants { + public static final String SEQUENCE_NAME = "hibernate_sequence"; + public static final String API_URL = "/api/1.0"; private Constants() { diff --git a/SpringApp/library/src/main/java/com/ip/library/core/error/NotFoundException.java b/SpringApp/library/src/main/java/com/ip/library/core/error/NotFoundException.java index ddabef7..028fc12 100644 --- a/SpringApp/library/src/main/java/com/ip/library/core/error/NotFoundException.java +++ b/SpringApp/library/src/main/java/com/ip/library/core/error/NotFoundException.java @@ -1,7 +1,7 @@ package com.ip.library.core.error; public class NotFoundException extends RuntimeException { - public NotFoundException(Long id) { - super(String.format("Entity with id [%s] is not found or not exists", id)); + public NotFoundException(Class clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); } } diff --git a/SpringApp/library/src/main/java/com/ip/library/core/model/BaseEntity.java b/SpringApp/library/src/main/java/com/ip/library/core/model/BaseEntity.java index 9e53df8..b942708 100644 --- a/SpringApp/library/src/main/java/com/ip/library/core/model/BaseEntity.java +++ b/SpringApp/library/src/main/java/com/ip/library/core/model/BaseEntity.java @@ -1,15 +1,23 @@ package com.ip.library.core.model; +import com.ip.library.core.configuration.Constants; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; + +@MappedSuperclass public abstract class BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME) + @SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1) protected Long id; protected BaseEntity() { } - protected BaseEntity(Long id) { - this.id = id; - } - public Long getId() { return id; } diff --git a/SpringApp/library/src/main/java/com/ip/library/core/repository/CommonRepository.java b/SpringApp/library/src/main/java/com/ip/library/core/repository/CommonRepository.java deleted file mode 100644 index e72aa9a..0000000 --- a/SpringApp/library/src/main/java/com/ip/library/core/repository/CommonRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.ip.library.core.repository; - -import java.util.List; - -public interface CommonRepository { - List getAll(); - - E get(T id); - - E create(E entity); - - E update(E entity); - - E delete(E entity); - - void deleteAll(); -} diff --git a/SpringApp/library/src/main/java/com/ip/library/core/repository/MapRepository.java b/SpringApp/library/src/main/java/com/ip/library/core/repository/MapRepository.java deleted file mode 100644 index e19f119..0000000 --- a/SpringApp/library/src/main/java/com/ip/library/core/repository/MapRepository.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.ip.library.core.repository; - -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import com.ip.library.core.model.BaseEntity; - -public abstract class MapRepository implements CommonRepository { - private final Map entities = new TreeMap<>(); - private Long lastId = 0L; - - protected MapRepository() { - } - - @Override - public List getAll() { - return entities.values().stream().toList(); - } - - @Override - public E get(Long id) { - return entities.get(id); - } - - @Override - public E create(E entity) { - lastId++; - entity.setId(lastId); - entities.put(lastId, entity); - return entity; - } - - @Override - public E update(E entity) { - if (get(entity.getId()) == null) { - return null; - } - entities.put(entity.getId(), entity); - return entity; - } - - @Override - public E delete(E entity) { - if (get(entity.getId()) == null) { - return null; - } - entities.remove(entity.getId()); - return entity; - } - - @Override - public void deleteAll() { - lastId = 0L; - entities.clear(); - } -} diff --git a/SpringApp/library/src/main/java/com/ip/library/types/api/TypeDto.java b/SpringApp/library/src/main/java/com/ip/library/types/api/TypeDto.java index e7626b9..49583bf 100644 --- a/SpringApp/library/src/main/java/com/ip/library/types/api/TypeDto.java +++ b/SpringApp/library/src/main/java/com/ip/library/types/api/TypeDto.java @@ -3,13 +3,15 @@ package com.ip.library.types.api; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; public class TypeDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Long id; @NotBlank + @Size(min = 5, max = 20) private String name; - - @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { return id; } diff --git a/SpringApp/library/src/main/java/com/ip/library/types/model/TypeEntity.java b/SpringApp/library/src/main/java/com/ip/library/types/model/TypeEntity.java index 4e945a4..9bd403d 100644 --- a/SpringApp/library/src/main/java/com/ip/library/types/model/TypeEntity.java +++ b/SpringApp/library/src/main/java/com/ip/library/types/model/TypeEntity.java @@ -4,15 +4,21 @@ import java.util.Objects; import com.ip.library.core.model.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "types") public class TypeEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 20) private String name; public TypeEntity() { super(); } - public TypeEntity(Long id, String name) { - super(id); + public TypeEntity(String name) { this.name = name; } diff --git a/SpringApp/library/src/main/java/com/ip/library/types/repository/TypeRepository.java b/SpringApp/library/src/main/java/com/ip/library/types/repository/TypeRepository.java index 1648303..1bf1394 100644 --- a/SpringApp/library/src/main/java/com/ip/library/types/repository/TypeRepository.java +++ b/SpringApp/library/src/main/java/com/ip/library/types/repository/TypeRepository.java @@ -1,10 +1,11 @@ package com.ip.library.types.repository; -import org.springframework.stereotype.Repository; +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; -import com.ip.library.core.repository.MapRepository; import com.ip.library.types.model.TypeEntity; -@Repository -public class TypeRepository extends MapRepository { +public interface TypeRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); } diff --git a/SpringApp/library/src/main/java/com/ip/library/types/service/TypeService.java b/SpringApp/library/src/main/java/com/ip/library/types/service/TypeService.java index 33f353f..526a337 100644 --- a/SpringApp/library/src/main/java/com/ip/library/types/service/TypeService.java +++ b/SpringApp/library/src/main/java/com/ip/library/types/service/TypeService.java @@ -1,9 +1,10 @@ package com.ip.library.types.service; import java.util.List; -import java.util.Optional; +import java.util.stream.StreamSupport; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.ip.library.core.error.NotFoundException; import com.ip.library.types.model.TypeEntity; @@ -17,27 +18,49 @@ public class TypeService { this.repository = repository; } + private void checkNameUniqueness(String name){ + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s already exists", name) + ); + } + } + + @Transactional(readOnly = true) public List getAll() { - return repository.getAll(); + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); } - public TypeEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); + @Transactional(readOnly = true) + public TypeEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(TypeEntity.class, id)); } + @Transactional public TypeEntity create(TypeEntity entity) { - return repository.create(entity); + if (entity == null) { + throw new IllegalArgumentException("Creating TypeEntity is null"); + } + checkNameUniqueness(entity.getName()); + return repository.save(entity); } + @Transactional public TypeEntity update(Long id, TypeEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Updating TypeEntity is null"); + } final TypeEntity existsEntity = get(id); + checkNameUniqueness(entity.getName()); existsEntity.setName(entity.getName()); - return repository.update(existsEntity); + return repository.save(existsEntity); } + @Transactional public TypeEntity delete(Long id) { final TypeEntity existsEntity = get(id); - return repository.delete(existsEntity); + repository.delete(existsEntity); + return existsEntity; } } diff --git a/SpringApp/library/src/main/java/com/ip/library/users/model/UserEntity.java b/SpringApp/library/src/main/java/com/ip/library/users/model/UserEntity.java index 7e9b559..c2eaac9 100644 --- a/SpringApp/library/src/main/java/com/ip/library/users/model/UserEntity.java +++ b/SpringApp/library/src/main/java/com/ip/library/users/model/UserEntity.java @@ -6,9 +6,18 @@ import java.util.Objects; import com.ip.library.books.model.BookEntity; import com.ip.library.core.model.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") public class UserEntity extends BaseEntity { - private String name; + @Column(nullable = false, unique = true, length = 20) + private String login; + @Column(nullable = false, unique = true, length = 20) private String password; + @Column(nullable = false, unique = true, length = 20) private String role; private List books; @@ -16,20 +25,19 @@ public class UserEntity extends BaseEntity { super(); } - public UserEntity(Long id, String name, String password, String role, List books) { - super(id); - this.name = name; + public UserEntity(String name, String password, String role, List books) { + this.login = name; this.password = password; this.role = role; this.books = books; } - public String getName() { - return name; + public String getLogin() { + return login; } - public void setName(String name) { - this.name = name; + public void setLogin(String name) { + this.login = name; } public String getPassword() { @@ -58,7 +66,7 @@ public class UserEntity extends BaseEntity { @Override public int hashCode() { - return Objects.hash(id, name, password, role); + return Objects.hash(id, login, password, role); } @Override @@ -69,7 +77,7 @@ public class UserEntity extends BaseEntity { return false; final UserEntity other = (UserEntity) obj; return Objects.equals(other.getId(), id) - && Objects.equals(other.getName(), name) + && Objects.equals(other.getLogin(), login) && Objects.equals(other.getPassword(), password) && Objects.equals(other.getRole(), role) && Objects.equals(other.getBooks(), books); diff --git a/SpringApp/library/src/main/java/com/ip/library/users/service/UserService.java b/SpringApp/library/src/main/java/com/ip/library/users/service/UserService.java index 1629e26..e7d4f28 100644 --- a/SpringApp/library/src/main/java/com/ip/library/users/service/UserService.java +++ b/SpringApp/library/src/main/java/com/ip/library/users/service/UserService.java @@ -39,7 +39,7 @@ public class UserService { public UserEntity update(Long id, UserEntity entity) { final UserEntity existsEntity = get(id); - existsEntity.setName(entity.getName()); + existsEntity.setLogin(entity.getLogin()); return repository.update(existsEntity); } diff --git a/SpringApp/library/src/test/java/com/ip/library/UsersTests.java b/SpringApp/library/src/test/java/com/ip/library/UsersTests.java index 1c3afc2..9659d43 100644 --- a/SpringApp/library/src/test/java/com/ip/library/UsersTests.java +++ b/SpringApp/library/src/test/java/com/ip/library/UsersTests.java @@ -57,7 +57,7 @@ class UsersTests { @Test void createTest() { Assertions.assertEquals(3, userService.getAll().size()); - Assertions.assertEquals("user3", user.getName()); + Assertions.assertEquals("user3", user.getLogin()); Assertions.assertEquals("aqw2sed45", user.getPassword()); Assertions.assertEquals("user", user.getRole()); Assertions.assertEquals(0, user.getBooks().size()); @@ -76,7 +76,7 @@ class UsersTests { if (user.getId() != 1L) testId = 1L; else testId = 2L; - final String testName = user.getName() + "TEST"; + final String testName = user.getLogin() + "TEST"; final String testPassword = user.getPassword() + "TEST"; final String testRole = "admin"; List testBooks = new ArrayList(); @@ -85,7 +85,7 @@ class UsersTests { testPassword, testRole, testBooks)); Assertions.assertEquals(3, userService.getAll().size()); Assertions.assertNotEquals(testId, user.getId()); - Assertions.assertEquals(testName, user.getName()); + Assertions.assertEquals(testName, user.getLogin()); Assertions.assertNotEquals(testPassword, user.getPassword()); Assertions.assertNotEquals(testRole, user.getRole()); Assertions.assertEquals(0, user.getBooks().size()); @@ -96,7 +96,7 @@ class UsersTests { userService.delete(user.getId()); Assertions.assertEquals(2, userService.getAll().size()); final UserEntity newEntity = userService.create(new UserEntity(null, - user.getName(), user.getPassword(), null, null)); + user.getLogin(), user.getPassword(), null, null)); Assertions.assertEquals(3, userService.getAll().size()); Assertions.assertNotEquals(user.getId(), newEntity.getId()); }