diff --git a/README.md b/README.md index 3b5421c..0d33003 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -**[Отчёт](https://git.is.ulstu.ru/olshab/PIbd-22_Shabunov_O.A._InternetProgramming_Backend/src/branch/Lab2/%D0%9E%D1%82%D1%87%D1%91%D1%82%20%28%D0%BB%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%20%E2%84%962%29.docx)** \ No newline at end of file +**[Отчёт](https://git.is.ulstu.ru/olshab/PIbd-22_Shabunov_O.A._InternetProgramming_Backend/src/branch/Lab3/%D0%9E%D1%82%D1%87%D1%91%D1%82%20%28%D0%BB%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BD%D0%B0%D1%8F%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%20%E2%84%962%29.docx)** \ No newline at end of file diff --git a/build.gradle b/build.gradle index b7867b9..42a3cac 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,23 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.2.3' + id 'org.springframework.boot' version '3.2.5' id 'io.spring.dependency-management' version '1.1.4' } group = 'com.example' 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,9 +28,13 @@ 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' + 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/src/main/java/com/example/demo/ageRatings/model/AgeRatingEntity.java b/src/main/java/com/example/demo/ageRatings/model/AgeRatingEntity.java index 8defbe8..db239e6 100644 --- a/src/main/java/com/example/demo/ageRatings/model/AgeRatingEntity.java +++ b/src/main/java/com/example/demo/ageRatings/model/AgeRatingEntity.java @@ -4,15 +4,20 @@ import java.util.Objects; import com.example.demo.core.model.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "ageRatings") public class AgeRatingEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 20) private String name; public AgeRatingEntity() { - super(); } - public AgeRatingEntity(Long id, String name) { - super(id); + public AgeRatingEntity(String name) { this.name = name; } @@ -33,11 +38,11 @@ public class AgeRatingEntity extends BaseEntity { public boolean equals(Object obj) { if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; - final AgeRatingEntity other = (AgeRatingEntity) obj; - return Objects.equals(other.getId(), id) - && Objects.equals(other.getName(), name); - } + final AgeRatingEntity other = (AgeRatingEntity) obj; + return Objects.equals(other.getId(), id) && Objects.equals(other.getName(), name); + } } diff --git a/src/main/java/com/example/demo/ageRatings/repository/AgeRatingRepository.java b/src/main/java/com/example/demo/ageRatings/repository/AgeRatingRepository.java index a781772..a63e73d 100644 --- a/src/main/java/com/example/demo/ageRatings/repository/AgeRatingRepository.java +++ b/src/main/java/com/example/demo/ageRatings/repository/AgeRatingRepository.java @@ -1,10 +1,10 @@ package com.example.demo.ageRatings.repository; -import org.springframework.stereotype.Repository; +import java.util.Optional; +import org.springframework.data.repository.CrudRepository; -import com.example.demo.core.repository.MapRepository; import com.example.demo.ageRatings.model.AgeRatingEntity; -@Repository -public class AgeRatingRepository extends MapRepository { +public interface AgeRatingRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); } diff --git a/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java b/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java index afd262e..9f36ae2 100644 --- a/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java +++ b/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java @@ -1,9 +1,10 @@ package com.example.demo.ageRatings.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.example.demo.ageRatings.model.AgeRatingEntity; import com.example.demo.ageRatings.repository.AgeRatingRepository; @@ -17,28 +18,40 @@ public class AgeRatingService { this.repository = repository; } + @Transactional(readOnly = true) public List getAll() { - return repository.getAll(); + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); } + @Transactional(readOnly = true) public AgeRatingEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(AgeRatingEntity.class, id)); } + @Transactional public AgeRatingEntity create(AgeRatingEntity entity) { - return repository.create(entity); + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + + return repository.save(entity); } + @Transactional public AgeRatingEntity update(Long id, AgeRatingEntity entity) { final AgeRatingEntity existsEntity = get(id); + existsEntity.setName(entity.getName()); - return repository.update(existsEntity); + return repository.save(existsEntity); } + @Transactional public AgeRatingEntity delete(Long id) { final AgeRatingEntity existsEntity = get(id); - return repository.delete(existsEntity); + repository.delete(existsEntity); + + return existsEntity; } public void deleteAll() { diff --git a/src/main/java/com/example/demo/core/configuration/Constants.java b/src/main/java/com/example/demo/core/configuration/Constants.java index d9c6b7c..ec97a88 100644 --- a/src/main/java/com/example/demo/core/configuration/Constants.java +++ b/src/main/java/com/example/demo/core/configuration/Constants.java @@ -1,6 +1,7 @@ package com.example.demo.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/src/main/java/com/example/demo/core/error/NotFoundException.java b/src/main/java/com/example/demo/core/error/NotFoundException.java index 586af3c..0787cb2 100644 --- a/src/main/java/com/example/demo/core/error/NotFoundException.java +++ b/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -1,7 +1,7 @@ package com.example.demo.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 inClass, Long id) { + super(String.format("%s with id [%s] is not found or not exists", inClass.getSimpleName(), id)); } } diff --git a/src/main/java/com/example/demo/core/model/BaseEntity.java b/src/main/java/com/example/demo/core/model/BaseEntity.java index 674ddfb..eba74ad 100644 --- a/src/main/java/com/example/demo/core/model/BaseEntity.java +++ b/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -1,15 +1,23 @@ package com.example.demo.core.model; +import com.example.demo.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/src/main/java/com/example/demo/core/repository/CommonRepository.java b/src/main/java/com/example/demo/core/repository/CommonRepository.java deleted file mode 100644 index 85e1e6d..0000000 --- a/src/main/java/com/example/demo/core/repository/CommonRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.demo.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/src/main/java/com/example/demo/core/repository/MapRepository.java b/src/main/java/com/example/demo/core/repository/MapRepository.java deleted file mode 100644 index 6809ac2..0000000 --- a/src/main/java/com/example/demo/core/repository/MapRepository.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.example.demo.core.repository; - -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import com.example.demo.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/src/main/java/com/example/demo/movies/model/MovieEntity.java b/src/main/java/com/example/demo/movies/model/MovieEntity.java index 0f41d5b..0765e32 100644 --- a/src/main/java/com/example/demo/movies/model/MovieEntity.java +++ b/src/main/java/com/example/demo/movies/model/MovieEntity.java @@ -4,30 +4,59 @@ import java.util.Objects; import com.example.demo.core.model.BaseEntity; import com.example.demo.types.model.TypeEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + import com.example.demo.ageRatings.model.AgeRatingEntity; +@Entity +@Table(name = "movies") public class MovieEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 30) private String title; + + @ManyToOne + @JoinColumn(name = "typeId", nullable = false) private TypeEntity type; - private boolean requiresSubscription; - private String poster; - private String description; - private Integer releaseDate; - private String country; - private String tagline; - private String director; + + @ManyToOne + @JoinColumn(name = "ageRatingId", nullable = false) private AgeRatingEntity ageRating; + + @Column(nullable = false) + private boolean requiresSubscription; + + @Column(length = 50) + private String poster; + + @Column(nullable = false, unique = true, length = 100) + private String description; + + private Integer releaseDate; + + @Column(nullable = false, unique = true, length = 20) + private String country; + + @Column(nullable = false, unique = true, length = 30) + private String tagline; + + @Column(nullable = false, unique = true, length = 30) + private String director; + + @Column(nullable = false, unique = true, length = 100) private String video; public MovieEntity() { - super(); } public MovieEntity(Long id, String title, TypeEntity type, boolean requiresSubscription, String poster, String description, Integer releaseDate, String country, String tagline, String director, AgeRatingEntity ageRating, String video) { - super(id); - this.title = title; this.type = type; this.requiresSubscription = requiresSubscription; diff --git a/src/main/java/com/example/demo/types/model/TypeEntity.java b/src/main/java/com/example/demo/types/model/TypeEntity.java index fd90bdb..4af5c92 100644 --- a/src/main/java/com/example/demo/types/model/TypeEntity.java +++ b/src/main/java/com/example/demo/types/model/TypeEntity.java @@ -4,15 +4,20 @@ import java.util.Objects; import com.example.demo.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; } @@ -33,11 +38,11 @@ public class TypeEntity extends BaseEntity { public boolean equals(Object obj) { if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; - final TypeEntity other = (TypeEntity) obj; - return Objects.equals(other.getId(), id) - && Objects.equals(other.getName(), name); - } + final TypeEntity other = (TypeEntity) obj; + return Objects.equals(other.getId(), id) && Objects.equals(other.getName(), name); + } } diff --git a/src/main/java/com/example/demo/types/repository/TypeRepository.java b/src/main/java/com/example/demo/types/repository/TypeRepository.java index 1c29ea2..31a2674 100644 --- a/src/main/java/com/example/demo/types/repository/TypeRepository.java +++ b/src/main/java/com/example/demo/types/repository/TypeRepository.java @@ -1,10 +1,10 @@ package com.example.demo.types.repository; -import org.springframework.stereotype.Repository; +import java.util.Optional; +import org.springframework.data.repository.CrudRepository; -import com.example.demo.core.repository.MapRepository; import com.example.demo.types.model.TypeEntity; -@Repository -public class TypeRepository extends MapRepository { +public interface TypeRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); } diff --git a/src/main/java/com/example/demo/types/service/TypeService.java b/src/main/java/com/example/demo/types/service/TypeService.java index a594bab..bbfaefc 100644 --- a/src/main/java/com/example/demo/types/service/TypeService.java +++ b/src/main/java/com/example/demo/types/service/TypeService.java @@ -1,9 +1,10 @@ package com.example.demo.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.example.demo.core.error.NotFoundException; import com.example.demo.types.model.TypeEntity; @@ -17,28 +18,40 @@ public class TypeService { this.repository = repository; } + @Transactional(readOnly = true) public List getAll() { - return repository.getAll(); + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); } + @Transactional(readOnly = true) public TypeEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(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("Entity is null"); + } + + return repository.save(entity); } + @Transactional public TypeEntity update(Long id, TypeEntity entity) { final TypeEntity existsEntity = get(id); + 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; } public void deleteAll() { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..b2320c6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,17 @@ +# Server +spring.main.banner-mode=off +server.port=8080 +# Logger settings +logging.level.com.example.demo=DEBUG + +# JPA settings +spring.datasource.url=jdbc:h2:file:./data +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false + +# H2 console +spring.h2.console.enabled=true diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..d5f355c --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Server +spring.main.banner-mode=off + +# Logger settings +# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF +logging.level.com.example.demo=DEBUG + +# JPA Settings +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false \ No newline at end of file diff --git a/Отчёт (лабораторная работа №2).docx b/Отчёт (лабораторная работа №3).docx similarity index 100% rename from Отчёт (лабораторная работа №2).docx rename to Отчёт (лабораторная работа №3).docx