diff --git a/demo/build.gradle b/demo/build.gradle index 92a39ba..e19679a 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -7,6 +7,17 @@ plugins { 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' } @@ -21,6 +32,9 @@ dependencies { 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/demo/data.mv.db b/demo/data.mv.db new file mode 100644 index 0000000..9876839 Binary files /dev/null and b/demo/data.mv.db differ diff --git a/demo/src/main/java/com/example/demo/DemoApplication.java b/demo/src/main/java/com/example/demo/DemoApplication.java index 708d069..9c8f20b 100644 --- a/demo/src/main/java/com/example/demo/DemoApplication.java +++ b/demo/src/main/java/com/example/demo/DemoApplication.java @@ -1,6 +1,7 @@ package com.example.demo; import java.time.LocalDateTime; +import java.util.List; import java.util.Objects; import org.slf4j.Logger; @@ -9,10 +10,10 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import com.example.demo.items.model.ItemEntity; -import com.example.demo.items.service.ItemService; import com.example.demo.messages.model.MessageEntity; import com.example.demo.messages.service.MessageService; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.service.OrderService; import com.example.demo.types.model.TypeEntity; import com.example.demo.types.service.TypeService; import com.example.demo.users.model.UserEntity; @@ -27,7 +28,7 @@ public class DemoApplication implements CommandLineRunner { private final TypeService typeService; // Бизнес-логика для сущности "Заказ" (Заказ, содержащий книги) - private final ItemService itemService; + private final OrderService orderService; // Бизнес-логика для сущности "Пользователь" private final UserService userService; @@ -36,9 +37,13 @@ public class DemoApplication implements CommandLineRunner { private final MessageService messageService; // Конструктор - public DemoApplication(TypeService typeService, ItemService itemService, UserService userService, MessageService messageService) { + public DemoApplication( + TypeService typeService, + OrderService orderService, + UserService userService, + MessageService messageService) { this.typeService = typeService; - this.itemService = itemService; + this.orderService = orderService; this.userService = userService; this.messageService = messageService; } @@ -52,32 +57,46 @@ public class DemoApplication implements CommandLineRunner { public void run(String... args) throws Exception { if (args.length > 0 && Objects.equals("--populate", args[0])) { log.info("Create default types values"); - final var type1 = typeService.create(new TypeEntity(null, "Protection")); - final var type2 = typeService.create(new TypeEntity(null, "Sharpness")); - final var type3 = typeService.create(new TypeEntity(null, "Infinity")); - - log.info("Create default items values"); - itemService.create(new ItemEntity(null, type1, 50.00, 20)); - itemService.create(new ItemEntity(null, type1, 12.00, 3)); - itemService.create(new ItemEntity(null, type2, 15.00, 30)); - itemService.create(new ItemEntity(null, type2, 64.00, 10)); - itemService.create(new ItemEntity(null, type2, 15.00, 6)); - itemService.create(new ItemEntity(null, type3, 80.00, 6)); - itemService.create(new ItemEntity(null, type3, 64.00, 3)); + final var type1 = typeService.create(new TypeEntity("Protection")); + final var type2 = typeService.create(new TypeEntity("Sharpness")); + final var type3 = typeService.create(new TypeEntity("Infinity")); log.info("Create default users values"); - final var user1 = userService.create(new UserEntity(null, "User1", "password1", "mail1@gmail.com")); - final var user2 = userService.create(new UserEntity(null, "User2", "password2", "mail2@gmail.com")); - final var user3 = userService.create(new UserEntity(null, "User3", "password3", "mail3@gmail.com")); + final var user1 = userService.create(new UserEntity("User1", "password", "mail1@gmail.com")); + final var user2 = userService.create(new UserEntity("User2", "password", "mail2@gmail.com")); + final var user3 = userService.create(new UserEntity("User3", "password", "mail3@gmail.com")); + + log.info("Create default order values"); + final var orders = List.of( + new OrderEntity(type1, 12.00, 3), + new OrderEntity(type1, 50.00, 20), + new OrderEntity(type2, 15.00, 30), + new OrderEntity(type2, 64.00, 10), + new OrderEntity(type2, 15.00, 6), + new OrderEntity(type3, 80.00, 6), + new OrderEntity(type3, 64.00, 3) + ); + orders.forEach(order -> orderService.create(user1.getId(), order)); log.info("Create default messages values"); - messageService.create(new MessageEntity(null, user1, "Message1", LocalDateTime.now(), false)); - messageService.create(new MessageEntity(null, user1, "Message2", LocalDateTime.now(), false)); - messageService.create(new MessageEntity(null, user2, "Message3", LocalDateTime.now(), false)); - messageService.create(new MessageEntity(null, user2, "Message4", LocalDateTime.now(), false)); - messageService.create(new MessageEntity(null, user2, "Message5", LocalDateTime.now(), false)); - messageService.create(new MessageEntity(null, user3, "Message6", LocalDateTime.now(), false)); - messageService.create(new MessageEntity(null, user3, "Message7", LocalDateTime.now(), false)); + final var messages1 = List.of( + new MessageEntity("Message1", LocalDateTime.now(), false), + new MessageEntity("Message2", LocalDateTime.now(), false), + new MessageEntity("Message3", LocalDateTime.now(), false) + ); + messages1.forEach(message -> messageService.create(user1.getId(), message)); + + final var messages2 = List.of( + new MessageEntity("Message4", LocalDateTime.now(), false), + new MessageEntity("Message5", LocalDateTime.now(), false) + ); + messages2.forEach(message -> messageService.create(user2.getId(), message)); + + final var messages3 = List.of( + new MessageEntity("Message6", LocalDateTime.now(), false), + new MessageEntity("Message7", LocalDateTime.now(), false) + ); + messages3.forEach(message -> messageService.create(user3.getId(), message)); } } } diff --git a/demo/src/main/java/com/example/demo/core/configuration/Constants.java b/demo/src/main/java/com/example/demo/core/configuration/Constants.java index 25c8372..8e0fc28 100644 --- a/demo/src/main/java/com/example/demo/core/configuration/Constants.java +++ b/demo/src/main/java/com/example/demo/core/configuration/Constants.java @@ -2,6 +2,9 @@ package com.example.demo.core.configuration; // Класс для задания констант public class Constants { + // Имя последовательности + public static final String SEQUENCE_NAME = "hibernate_sequence"; + // Базовый префикс REST-API public static final String API_URL = "/api/1.0"; diff --git a/demo/src/main/java/com/example/demo/core/error/NotFoundException.java b/demo/src/main/java/com/example/demo/core/error/NotFoundException.java index d9990ea..a9261ed 100644 --- a/demo/src/main/java/com/example/demo/core/error/NotFoundException.java +++ b/demo/src/main/java/com/example/demo/core/error/NotFoundException.java @@ -2,7 +2,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 clazz, Long id) { + super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id)); } } diff --git a/demo/src/main/java/com/example/demo/core/model/BaseEntity.java b/demo/src/main/java/com/example/demo/core/model/BaseEntity.java index 99ffa33..fc1852e 100644 --- a/demo/src/main/java/com/example/demo/core/model/BaseEntity.java +++ b/demo/src/main/java/com/example/demo/core/model/BaseEntity.java @@ -1,19 +1,26 @@ 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/demo/src/main/java/com/example/demo/core/repository/CommonRepository.java b/demo/src/main/java/com/example/demo/core/repository/CommonRepository.java deleted file mode 100644 index 96976ac..0000000 --- a/demo/src/main/java/com/example/demo/core/repository/CommonRepository.java +++ /dev/null @@ -1,24 +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/demo/src/main/java/com/example/demo/core/repository/MapRepository.java b/demo/src/main/java/com/example/demo/core/repository/MapRepository.java deleted file mode 100644 index 06e6136..0000000 --- a/demo/src/main/java/com/example/demo/core/repository/MapRepository.java +++ /dev/null @@ -1,68 +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/demo/src/main/java/com/example/demo/items/api/ItemController.java b/demo/src/main/java/com/example/demo/items/api/ItemController.java deleted file mode 100644 index 5e01771..0000000 --- a/demo/src/main/java/com/example/demo/items/api/ItemController.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.example.demo.items.api; - -import java.util.List; - -import org.modelmapper.ModelMapper; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.example.demo.core.configuration.Constants; -import com.example.demo.items.model.ItemEntity; -import com.example.demo.items.service.ItemService; -import com.example.demo.types.service.TypeService; - -import jakarta.validation.Valid; - -// Контроллер для сущности "Заказ" (Заказ, содержащий книги) -@RestController -@RequestMapping(Constants.API_URL + "/item") -public class ItemController { - // Бизнес-логика для сущности "Заказ" (Заказ, содержащий книги) - private final ItemService itemService; - - // Бизнес-логика для сущности "Тип" (Тип книги) - private final TypeService typeService; - - // Библиотека для преобразования сущности - private final ModelMapper modelMapper; - - // Конструктор - public ItemController(ItemService itemService, TypeService typeService, ModelMapper modelMapper) { - this.itemService = itemService; - this.typeService = typeService; - this.modelMapper = modelMapper; - } - - // Преобразовать из сущности в DTO - private ItemDto toDto(ItemEntity entity) { - return modelMapper.map(entity, ItemDto.class); - } - - // Преобразовать из DTO в сущность - private ItemEntity toEntity(@Valid ItemDto dto) { - final ItemEntity entity = modelMapper.map(dto, ItemEntity.class); - entity.setType(typeService.get(dto.getTypeId())); - return entity; - } - - // Получить все элементы или по заданному фильтру - @GetMapping - public List getAll(@RequestParam(name = "typeId", defaultValue = "0") Long typeId) { - return itemService.getAll(typeId).stream().map(this::toDto).toList(); - } - - // Получить элемент по идентификатору - @GetMapping("/{id}") - public ItemDto get(@PathVariable(name = "id") Long id) { - return toDto(itemService.get(id)); - } - - // Создать элемент - @PostMapping - public ItemDto create(@RequestBody @Valid ItemDto dto) { - return toDto(itemService.create(toEntity(dto))); - } - - // Изменить элемент - @PutMapping("/{id}") - public ItemDto update(@PathVariable(name = "id") Long id, @RequestBody @Valid ItemDto dto) { - return toDto(itemService.update(id, toEntity(dto))); - } - - // Удалить элемент - @DeleteMapping("/{id}") - public ItemDto delete(@PathVariable(name = "id") Long id) { - return toDto(itemService.delete(id)); - } -} diff --git a/demo/src/main/java/com/example/demo/items/repository/ItemRepository.java b/demo/src/main/java/com/example/demo/items/repository/ItemRepository.java deleted file mode 100644 index 61da1e4..0000000 --- a/demo/src/main/java/com/example/demo/items/repository/ItemRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.demo.items.repository; - -import org.springframework.stereotype.Repository; - -import com.example.demo.core.repository.MapRepository; -import com.example.demo.items.model.ItemEntity; - -// Хранилище для сущности "Заказ" (Заказ, содержащий книги) -@Repository -public class ItemRepository extends MapRepository { -} diff --git a/demo/src/main/java/com/example/demo/items/service/ItemService.java b/demo/src/main/java/com/example/demo/items/service/ItemService.java deleted file mode 100644 index 9c19f7d..0000000 --- a/demo/src/main/java/com/example/demo/items/service/ItemService.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.example.demo.items.service; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import org.springframework.stereotype.Service; - -import com.example.demo.core.error.NotFoundException; -import com.example.demo.items.model.ItemEntity; -import com.example.demo.items.repository.ItemRepository; - -// Бизнес-логика для сущности "Заказ" (Заказ, содержащий книги) -@Service -public class ItemService { - // Хранилище элементов - private final ItemRepository repository; - - // Конструктор - public ItemService(ItemRepository repository) { - this.repository = repository; - } - - // Получить все элементы или по заданному фильтру - public List getAll(Long typeId) { - if (Objects.equals(typeId, 0L)) { - return repository.getAll(); - } - return repository.getAll().stream() - .filter(item -> item.getType().getId().equals(typeId)) - .toList(); - } - - // Получить элемент по идентификатору - public ItemEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); - } - - // Создать элемент - public ItemEntity create(ItemEntity entity) { - return repository.create(entity); - } - - // Изменить элемент - public ItemEntity update(Long id, ItemEntity entity) { - final ItemEntity existsEntity = get(id); - existsEntity.setType(entity.getType()); - existsEntity.setPrice(entity.getPrice()); - existsEntity.setCount(entity.getCount()); - return repository.update(existsEntity); - } - - // Удалить элемент - public ItemEntity delete(Long id) { - final ItemEntity existsEntity = get(id); - return repository.delete(existsEntity); - } - - // Удалить все элементы - public void deleteAll() { - repository.deleteAll(); - } -} diff --git a/demo/src/main/java/com/example/demo/messages/api/MessageController.java b/demo/src/main/java/com/example/demo/messages/api/MessageController.java index 3fb75c9..66f12ed 100644 --- a/demo/src/main/java/com/example/demo/messages/api/MessageController.java +++ b/demo/src/main/java/com/example/demo/messages/api/MessageController.java @@ -10,33 +10,27 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.example.demo.core.configuration.Constants; import com.example.demo.messages.model.MessageEntity; import com.example.demo.messages.service.MessageService; -import com.example.demo.users.service.UserService; import jakarta.validation.Valid; // Контроллер для сущности "Сообщение" @RestController -@RequestMapping(Constants.API_URL + "/message") +@RequestMapping(Constants.API_URL + "/user/{user}/message") public class MessageController { // Бизнес-логика для сущности "Сообщение" private final MessageService messageService; - // Бизнес-логика для сущности "Пользователь" - private final UserService userService; - // Библиотека для преобразования сущности private final ModelMapper modelMapper; // Конструктор - public MessageController(MessageService messageService, UserService userService, ModelMapper modelMapper) { + public MessageController(MessageService messageService, ModelMapper modelMapper) { this.messageService = messageService; - this.userService = userService; this.modelMapper = modelMapper; } @@ -48,37 +42,47 @@ public class MessageController { // Преобразовать из DTO в сущность private MessageEntity toEntity(@Valid MessageDto dto) { final MessageEntity entity = modelMapper.map(dto, MessageEntity.class); - entity.setSender(userService.get(dto.getSenderId())); return entity; } // Получить все элементы @GetMapping - public List getAll(@RequestParam(name = "senderId", defaultValue = "0") Long senderId) { - return messageService.getAll(senderId).stream().map(this::toDto).toList(); + public List getAll(@PathVariable(name = "user") Long userId) { + return messageService.getAll(userId).stream() + .map(this::toDto) + .toList(); } // Получить элемент по идентификатору @GetMapping("/{id}") - public MessageDto get(@PathVariable(name = "id") Long id) { - return toDto(messageService.get(id)); + public MessageDto get( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id) { + return toDto(messageService.get(userId, id)); } // Создать элемент @PostMapping - public MessageDto create(@RequestBody @Valid MessageDto dto) { - return toDto(messageService.create(toEntity(dto))); + public MessageDto create( + @PathVariable(name = "user") Long userId, + @RequestBody @Valid MessageDto dto) { + return toDto(messageService.create(userId, toEntity(dto))); } // Изменить элемент @PutMapping("/{id}") - public MessageDto update(@PathVariable(name = "id") Long id, @RequestBody @Valid MessageDto dto) { - return toDto(messageService.update(id, toEntity(dto))); + public MessageDto update( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id, + @RequestBody @Valid MessageDto dto) { + return toDto(messageService.update(userId, id, toEntity(dto))); } // Удалить элемент @DeleteMapping("/{id}") - public MessageDto delete(@PathVariable(name = "id") Long id) { - return toDto(messageService.delete(id)); + public MessageDto delete( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id) { + return toDto(messageService.delete(userId, id)); } } diff --git a/demo/src/main/java/com/example/demo/messages/api/MessageDto.java b/demo/src/main/java/com/example/demo/messages/api/MessageDto.java index 030394d..a04db34 100644 --- a/demo/src/main/java/com/example/demo/messages/api/MessageDto.java +++ b/demo/src/main/java/com/example/demo/messages/api/MessageDto.java @@ -17,7 +17,7 @@ public class MessageDto { // Идентификатор отправителя сообщения @NotNull @Min(1) - private Long senderId; + private Long userId; // Текст сообщения @NotBlank @@ -41,13 +41,13 @@ public class MessageDto { } // Получить идентификатор отправителя сообщения - public Long getSenderId() { - return senderId; + public Long getUserId() { + return userId; } // Установить идентификатор отправителя сообщения - public void setSenderId(Long senderId) { - this.senderId = senderId; + public void setUserId(Long userId) { + this.userId = userId; } // Получить текст сообщения diff --git a/demo/src/main/java/com/example/demo/messages/model/MessageEntity.java b/demo/src/main/java/com/example/demo/messages/model/MessageEntity.java index 7278933..c237b5c 100644 --- a/demo/src/main/java/com/example/demo/messages/model/MessageEntity.java +++ b/demo/src/main/java/com/example/demo/messages/model/MessageEntity.java @@ -6,42 +6,55 @@ import java.util.Objects; import com.example.demo.core.model.BaseEntity; import com.example.demo.users.model.UserEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Column; + // Сущность "Сообщение" +@Entity +@Table(name = "messages") public class MessageEntity extends BaseEntity { // Отправитель сообщения - private UserEntity sender; + @ManyToOne + @JoinColumn(name = "userId", nullable = false) + private UserEntity user; // Текст сообщения + @Column(nullable = false) private String text; // Дата отправки + @Column(nullable = false) private LocalDateTime date; // Признак публикации сообщения + @Column(nullable = false) private boolean isPublished; // Конструктор по умолчанию public MessageEntity() { - super(); } // Конструктор с параметрами для создания объекта - public MessageEntity(Long id, UserEntity sender, String text, LocalDateTime date, boolean isPublished) { - super(id); - this.sender = sender; + public MessageEntity(String text, LocalDateTime date, boolean isPublished) { this.text = text; this.date = date; this.isPublished = isPublished; } // Получить отправителя - public UserEntity getSender() { - return sender; + public UserEntity getUser() { + return user; } // Установить отправителя - public void setSender(UserEntity sender) { - this.sender = sender; + public void setUser(UserEntity user) { + this.user = user; + if (!user.getMessages().contains(this)) { + user.getMessages().add(this); + } } // Получить текст сообщения @@ -77,7 +90,7 @@ public class MessageEntity extends BaseEntity { // Получить хэш-код объекта @Override public int hashCode() { - return Objects.hash(id, sender, text, date, isPublished); + return Objects.hash(id, user.getId(), text, date, isPublished); } // Сравнить объекты @@ -89,7 +102,7 @@ public class MessageEntity extends BaseEntity { return false; final MessageEntity other = (MessageEntity) obj; return Objects.equals(other.getId(), id) - && Objects.equals(other.getSender(), sender) + && Objects.equals(other.getUser().getId(), user.getId()) && Objects.equals(other.getText(), text) && Objects.equals(other.getDate(), date) && Objects.equals(other.getIsPublished(), isPublished); diff --git a/demo/src/main/java/com/example/demo/messages/repository/MessageRepository.java b/demo/src/main/java/com/example/demo/messages/repository/MessageRepository.java index f67f587..50c5325 100644 --- a/demo/src/main/java/com/example/demo/messages/repository/MessageRepository.java +++ b/demo/src/main/java/com/example/demo/messages/repository/MessageRepository.java @@ -1,11 +1,17 @@ package com.example.demo.messages.repository; -import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; -import com.example.demo.core.repository.MapRepository; import com.example.demo.messages.model.MessageEntity; // Хранилище для сущности "Сообщение" -@Repository -public class MessageRepository extends MapRepository { +public interface MessageRepository extends CrudRepository { + // Получить сообщение по пользователю и идентификатору + Optional findOnyByUserIdAndId(Long userId, Long id); + + // Получить список сообщений по пользователю + List findByUserId(Long userId); } diff --git a/demo/src/main/java/com/example/demo/messages/service/MessageService.java b/demo/src/main/java/com/example/demo/messages/service/MessageService.java index 0c59e5c..769946d 100644 --- a/demo/src/main/java/com/example/demo/messages/service/MessageService.java +++ b/demo/src/main/java/com/example/demo/messages/service/MessageService.java @@ -1,14 +1,16 @@ package com.example.demo.messages.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.example.demo.core.error.NotFoundException; import com.example.demo.messages.model.MessageEntity; import com.example.demo.messages.repository.MessageRepository; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; // Бизнес-логика для сущности "Сообщение" @Service @@ -16,47 +18,67 @@ public class MessageService { // Хранилище элементов private final MessageRepository repository; + // Бизнес-логика для отправителей (пользователей) + private final UserService userService; + // Конструктор - public MessageService(MessageRepository repository) { + public MessageService(MessageRepository repository, UserService userService) { this.repository = repository; + this.userService = userService; } // Получить все элементы или по заданному фильтру - public List getAll(Long senderId) { - if (Objects.equals(senderId, 0L)) { - return repository.getAll(); + @Transactional(readOnly = true) + public List getAll(Long userId) { + if (userId <= 0L) { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + else { + userService.get(userId); + return repository.findByUserId(userId); } - return repository.getAll().stream() - .filter(item -> item.getSender().getId().equals(senderId)) - .toList(); } // Получить элемент по идентификатору - public MessageEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); + @Transactional(readOnly = true) + public MessageEntity get(Long userId, Long id) { + userService.get(userId); + return repository.findOnyByUserIdAndId(userId, id) + .orElseThrow(() -> new NotFoundException(MessageEntity.class, id)); } // Создать элемент - public MessageEntity create(MessageEntity entity) { - return repository.create(entity); + @Transactional + public MessageEntity create(Long userId, MessageEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + final UserEntity existsEntity = userService.get(userId); + entity.setUser(existsEntity); + return repository.save(entity); } // Изменить элемент - public MessageEntity update(Long id, MessageEntity entity) { - final MessageEntity existsEntity = get(id); - existsEntity.setSender(entity.getSender()); + @Transactional + public MessageEntity update(Long userId, Long id, MessageEntity entity) { + userService.get(userId); + final MessageEntity existsEntity = get(userId, id); + existsEntity.setUser(entity.getUser()); existsEntity.setText(entity.getText()); - return repository.update(existsEntity); + return repository.save(existsEntity); } // Удалить элемент - public MessageEntity delete(Long id) { - final MessageEntity existsEntity = get(id); - return repository.delete(existsEntity); + @Transactional + public MessageEntity delete(Long userId, Long id) { + userService.get(userId); + final MessageEntity existsEntity = get(userId, id); + repository.delete(existsEntity); + return existsEntity; } // Удалить все элементы + @Transactional public void deleteAll() { repository.deleteAll(); } diff --git a/demo/src/main/java/com/example/demo/orders/api/OrderController.java b/demo/src/main/java/com/example/demo/orders/api/OrderController.java new file mode 100644 index 0000000..ba2ca92 --- /dev/null +++ b/demo/src/main/java/com/example/demo/orders/api/OrderController.java @@ -0,0 +1,111 @@ +package com.example.demo.orders.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; +import com.example.demo.orders.service.OrderService; +import com.example.demo.types.service.TypeService; + +import jakarta.validation.Valid; + +// Контроллер для сущности "Заказ" (Заказ, содержащий книги) +@RestController +@RequestMapping(Constants.API_URL + "/user/{user}/order") +public class OrderController { + // Бизнес-логика для сущности "Заказ" (Заказ, содержащий книги) + private final OrderService orderService; + + // Бизнес-логика для сущности "Тип" (Тип книги) + private final TypeService typeService; + + // Библиотека для преобразования сущности + private final ModelMapper modelMapper; + + // Конструктор + public OrderController(OrderService orderService, TypeService typeService, ModelMapper modelMapper) { + this.orderService = orderService; + this.typeService = typeService; + this.modelMapper = modelMapper; + } + + // Преобразовать из сущности в DTO + private OrderDto toDto(OrderEntity entity) { + return modelMapper.map(entity, OrderDto.class); + } + + // Преобразовать из DTO в сущность + private OrderEntity toEntity(@Valid OrderDto dto) { + final OrderEntity entity = modelMapper.map(dto, OrderEntity.class); + entity.setType(typeService.get(dto.getTypeId())); + return entity; + } + + // Преобрзовать из сущности сгруппированных заказов в DTO + private OrderGroupedDto toGroupedDto(OrderGrouped entity) { + return modelMapper.map(entity, OrderGroupedDto.class); + } + + // Получить все элементы или по заданному фильтру + @GetMapping + public List getAll( + @PathVariable(name = "user") Long userId, + @RequestParam(name = "typeId", defaultValue = "0") Long typeId) { + return orderService.getAll(userId, typeId).stream() + .map(this::toDto) + .toList(); + } + + // Получить элемент по идентификатору + @GetMapping("/{id}") + public OrderDto get( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id) { + return toDto(orderService.get(userId, id)); + } + + // Создать элемент + @PostMapping + public OrderDto create( + @PathVariable(name = "user") Long userId, + @RequestBody @Valid OrderDto dto) { + return toDto(orderService.create(userId, toEntity(dto))); + } + + // Изменить элемент + @PutMapping("/{id}") + public OrderDto update( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id, + @RequestBody @Valid OrderDto dto) { + return toDto(orderService.update(userId, id, toEntity(dto))); + } + + // Удалить элемент + @DeleteMapping("/{id}") + public OrderDto delete( + @PathVariable(name = "user") Long userId, + @PathVariable(name = "id") Long id) { + return toDto(orderService.delete(userId, id)); + } + + // Получить список заказов, сгруппированных по типу + @GetMapping("/total") + public List getMethodName(@PathVariable(name = "user") Long userId) { + return orderService.getTotal(userId).stream() + .map(this::toGroupedDto) + .toList(); + } +} diff --git a/demo/src/main/java/com/example/demo/items/api/ItemDto.java b/demo/src/main/java/com/example/demo/orders/api/OrderDto.java similarity index 96% rename from demo/src/main/java/com/example/demo/items/api/ItemDto.java rename to demo/src/main/java/com/example/demo/orders/api/OrderDto.java index 34b49c0..f3acbbf 100644 --- a/demo/src/main/java/com/example/demo/items/api/ItemDto.java +++ b/demo/src/main/java/com/example/demo/orders/api/OrderDto.java @@ -1,4 +1,4 @@ -package com.example.demo.items.api; +package com.example.demo.orders.api; import com.fasterxml.jackson.annotation.JsonProperty; @@ -6,7 +6,7 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; // DTO для сущности "Заказ" (Заказ, содержащий книги) -public class ItemDto { +public class OrderDto { // Идентфикатор @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Long id; diff --git a/demo/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java b/demo/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java new file mode 100644 index 0000000..7257cce --- /dev/null +++ b/demo/src/main/java/com/example/demo/orders/api/OrderGroupedDto.java @@ -0,0 +1,43 @@ +package com.example.demo.orders.api; + +// DTO для заказов, сгруппированных по типу +public class OrderGroupedDto { + // Идентификатор типа + private Long typeId; + + // Общая стоимость заказов + private Long totalPrice; + + // Общее количество заказов + private Integer totalCount; + + // Получить идентификатор типа + public Long getTypeId() { + return typeId; + } + + // Установить идентификатор типа + public void setTypeId(Long typeId) { + this.typeId = typeId; + } + + // Получить общую стоимость заказов + public Long getTotalPrice() { + return totalPrice; + } + + // Установить общую стоимость заказов + public void setTotalPrice(Long totalPrice) { + this.totalPrice = totalPrice; + } + + // Получить общее количество заказов + public Integer getTotalCount() { + return totalCount; + } + + // Установить общее количество заказов + public void setTotalCount(Integer totalCount) { + this.totalCount = totalCount; + } +} diff --git a/demo/src/main/java/com/example/demo/items/model/ItemEntity.java b/demo/src/main/java/com/example/demo/orders/model/OrderEntity.java similarity index 59% rename from demo/src/main/java/com/example/demo/items/model/ItemEntity.java rename to demo/src/main/java/com/example/demo/orders/model/OrderEntity.java index ba7e67e..f2d502d 100644 --- a/demo/src/main/java/com/example/demo/items/model/ItemEntity.java +++ b/demo/src/main/java/com/example/demo/orders/model/OrderEntity.java @@ -1,29 +1,45 @@ -package com.example.demo.items.model; +package com.example.demo.orders.model; import java.util.Objects; import com.example.demo.core.model.BaseEntity; import com.example.demo.types.model.TypeEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; // Сущность "Заказ" (Заказ, содержащий книги) -public class ItemEntity extends BaseEntity { +@Entity +@Table(name = "orders") +public class OrderEntity extends BaseEntity { // Тип книги + @ManyToOne + @JoinColumn(name = "typeId", nullable = false) private TypeEntity type; + // Заказчик (пользователь) + @ManyToOne + @JoinColumn(name = "userId", nullable = false) + private UserEntity user; + // Цена книги + @Column(nullable = false) private Double price; // Количество книг + @Column(nullable = false) private Integer count; // Конструктор по умолчанию - public ItemEntity() { - super(); + public OrderEntity() { } // Конструктор с параметрами для создания объекта - public ItemEntity(Long id, TypeEntity type, Double price, Integer count) { - super(id); + public OrderEntity(TypeEntity type, Double price, Integer count) { this.type = type; this.price = price; this.count = count; @@ -39,6 +55,19 @@ public class ItemEntity extends BaseEntity { this.type = type; } + // Получить заказчика (пользователя) + public UserEntity getUser() { + return user; + } + + // Установить заказчика (пользователя) + public void setUser(UserEntity user) { + this.user = user; + if (!user.getOrders().contains(this)) { + user.getOrders().add(this); + } + } + // Получить стоимость книги public Double getPrice() { return price; @@ -62,7 +91,7 @@ public class ItemEntity extends BaseEntity { // Получить хэш-код объекта @Override public int hashCode() { - return Objects.hash(id, type, price, count); + return Objects.hash(id, type, user.getId(), price, count); } // Сравнить объекты @@ -72,9 +101,10 @@ public class ItemEntity extends BaseEntity { return true; if (obj == null || getClass() != obj.getClass()) return false; - final ItemEntity other = (ItemEntity) obj; + final OrderEntity other = (OrderEntity) obj; return Objects.equals(other.getId(), id) && Objects.equals(other.getType(), type) + && Objects.equals(other.getUser().getId(), user.getId()) && Objects.equals(other.getPrice(), price) && Objects.equals(other.getCount(), count); } diff --git a/demo/src/main/java/com/example/demo/orders/model/OrderGrouped.java b/demo/src/main/java/com/example/demo/orders/model/OrderGrouped.java new file mode 100644 index 0000000..ca53f8a --- /dev/null +++ b/demo/src/main/java/com/example/demo/orders/model/OrderGrouped.java @@ -0,0 +1,15 @@ +package com.example.demo.orders.model; + +import com.example.demo.types.model.TypeEntity; + +// Заказы, сгруппированные по типу +public interface OrderGrouped { + // Тип заказов + TypeEntity getType(); + + // Общая сумма заказов + double getTotalPrice(); + + // Общее количество заказов + int getTotalCount(); +} diff --git a/demo/src/main/java/com/example/demo/orders/repository/OrderRepository.java b/demo/src/main/java/com/example/demo/orders/repository/OrderRepository.java new file mode 100644 index 0000000..84a8189 --- /dev/null +++ b/demo/src/main/java/com/example/demo/orders/repository/OrderRepository.java @@ -0,0 +1,38 @@ +package com.example.demo.orders.repository; + +import java.util.Optional; +import java.util.List; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.jpa.repository.Query; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; + +// Хранилище для сущности "Заказ" (Заказ, содержащий книги) +public interface OrderRepository extends CrudRepository { + // Получить заказ по пользователю и идентификатору + Optional findOneByUserIdAndId(Long userId, Long id); + + // Получить список заказов по пользователю + List findByUserId(Long userId); + + // Получить список заказов по типу + List findByUserIdAndTypeId(Long userId, Long typeId); + + // Получить заказы, сгруппированные по типу + // select + // type.name, + // coalesce(sum(order.price), 0), + // coalesce(sum(order.count), 0) + // from types as type + // left join orders as order on type.id = order.type_id and order.user_id = ? + // group by type.name order by type.id + @Query("select " + + "t as type, " + + "coalesce(sum(o.price), 0) as totalPrice, " + + "coalesce(sum(o.count), 0) as totalCount " + + "from TypeEntity t left join OrderEntity o on o.type = t and o.user.id = ?1 " + + "group by t order by t.id") + List getOrdersTotalByType(long userId); +} diff --git a/demo/src/main/java/com/example/demo/orders/service/OrderService.java b/demo/src/main/java/com/example/demo/orders/service/OrderService.java new file mode 100644 index 0000000..b00b172 --- /dev/null +++ b/demo/src/main/java/com/example/demo/orders/service/OrderService.java @@ -0,0 +1,93 @@ +package com.example.demo.orders.service; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.model.OrderGrouped; +import com.example.demo.orders.repository.OrderRepository; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +// Бизнес-логика для сущности "Заказ" (Заказ, содержащий книги) +@Service +public class OrderService { + // Хранилище элементов + private final OrderRepository repository; + + // Бизнес-логика для заказчиков (пользователей) + private final UserService userService; + + // Конструктор + public OrderService(OrderRepository repository, UserService userService) { + this.repository = repository; + this.userService = userService; + } + + // Получить все элементы или по заданному фильтру + @Transactional(readOnly = true) + public List getAll(Long userId, Long typeId) { + userService.get(userId); + if (typeId <= 0L) { + return repository.findByUserId(userId); + } + else { + return repository.findByUserIdAndTypeId(userId, typeId); + } + } + + // Получить элемент по идентификатору + @Transactional(readOnly = true) + public OrderEntity get(Long userId, Long id) { + userService.get(userId); + return repository.findOneByUserIdAndId(userId, id) + .orElseThrow(() -> new NotFoundException(OrderEntity.class, id)); + } + + // Создать элемент + @Transactional + public OrderEntity create(Long userId, OrderEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + final UserEntity existsUser = userService.get(userId); + entity.setUser(existsUser); + return repository.save(entity); + } + + // Изменить элемент + @Transactional + public OrderEntity update(Long userId, Long id, OrderEntity entity) { + userService.get(userId); + final OrderEntity existsEntity = get(userId, id); + existsEntity.setType(entity.getType()); + existsEntity.setPrice(entity.getPrice()); + existsEntity.setCount(entity.getCount()); + return repository.save(existsEntity); + } + + // Удалить элемент + @Transactional + public OrderEntity delete(Long userId, Long id) { + userService.get(userId); + final OrderEntity existsEntity = get(userId, id); + repository.delete(existsEntity); + return existsEntity; + } + + // Удалить все элементы + @Transactional + public void deleteAll() { + repository.deleteAll(); + } + + // Получить список заказов, сгруппированных по типу + @Transactional(readOnly = true) + public List getTotal(long userId) { + userService.get(userId); + return repository.getOrdersTotalByType(userId); + } +} diff --git a/demo/src/main/java/com/example/demo/speaker/api/SpeakerController.java b/demo/src/main/java/com/example/demo/speaker/api/SpeakerController.java deleted file mode 100644 index 293296d..0000000 --- a/demo/src/main/java/com/example/demo/speaker/api/SpeakerController.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.example.demo.speaker.api; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.example.demo.speaker.service.SpeakerService; - -@RestController -public class SpeakerController { - private final SpeakerService speakerService; - - public SpeakerController(SpeakerService speakerService) { - this.speakerService = speakerService; - } - - @GetMapping - public String hello( - @RequestParam(value = "name", defaultValue = "Мир") String name, - @RequestParam(value = "lang", defaultValue = "ru") String lang) { - return speakerService.say(name, lang); - } -} diff --git a/demo/src/main/java/com/example/demo/speaker/configuration/SpeakerConfiguration.java b/demo/src/main/java/com/example/demo/speaker/configuration/SpeakerConfiguration.java deleted file mode 100644 index 0ef7cda..0000000 --- a/demo/src/main/java/com/example/demo/speaker/configuration/SpeakerConfiguration.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.example.demo.speaker.configuration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.example.demo.speaker.domain.Speaker; -import com.example.demo.speaker.domain.SpeakerEng; -import com.example.demo.speaker.domain.SpeakerRus; - -@Configuration -public class SpeakerConfiguration { - private final Logger log = LoggerFactory.getLogger(SpeakerConfiguration.class); - - @Bean(value = "ru", initMethod = "init", destroyMethod = "destroy") - public SpeakerRus createRusSpeaker() { - log.info("Call createRusSpeaker()"); - return new SpeakerRus(); - } - - @Bean(value = "en") - public Speaker createEngSpeaker() { - log.info("Call createEngSpeaker()"); - return new SpeakerEng(); - } -} diff --git a/demo/src/main/java/com/example/demo/speaker/domain/Speaker.java b/demo/src/main/java/com/example/demo/speaker/domain/Speaker.java deleted file mode 100644 index 27a0a29..0000000 --- a/demo/src/main/java/com/example/demo/speaker/domain/Speaker.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.demo.speaker.domain; - -public interface Speaker { - String say(); -} diff --git a/demo/src/main/java/com/example/demo/speaker/domain/SpeakerDeu.java b/demo/src/main/java/com/example/demo/speaker/domain/SpeakerDeu.java deleted file mode 100644 index 5909864..0000000 --- a/demo/src/main/java/com/example/demo/speaker/domain/SpeakerDeu.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.example.demo.speaker.domain; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; - -@Component(value = "de") -public class SpeakerDeu implements Speaker { - private final Logger log = LoggerFactory.getLogger(SpeakerDeu.class); - - @Override - public String say() { - return "Hallo"; - } - - @PostConstruct - public void init() { - log.info("SpeakerDeu.init()"); - } - - @PreDestroy - public void destroy() { - log.info("SpeakerDeu.destroy()"); - } -} diff --git a/demo/src/main/java/com/example/demo/speaker/domain/SpeakerEng.java b/demo/src/main/java/com/example/demo/speaker/domain/SpeakerEng.java deleted file mode 100644 index a310403..0000000 --- a/demo/src/main/java/com/example/demo/speaker/domain/SpeakerEng.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.demo.speaker.domain; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; - -public class SpeakerEng implements Speaker, InitializingBean, DisposableBean { - private final Logger log = LoggerFactory.getLogger(SpeakerEng.class); - - @Override - public String say() { - return "Hello"; - } - - @Override - public void afterPropertiesSet() { - log.info("SpeakerEng.afterPropertiesSet()"); - } - - @Override - public void destroy() { - log.info("SpeakerEng.destroy()"); - - } -} diff --git a/demo/src/main/java/com/example/demo/speaker/domain/SpeakerRus.java b/demo/src/main/java/com/example/demo/speaker/domain/SpeakerRus.java deleted file mode 100644 index d6ed18e..0000000 --- a/demo/src/main/java/com/example/demo/speaker/domain/SpeakerRus.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.example.demo.speaker.domain; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SpeakerRus implements Speaker { - private final Logger log = LoggerFactory.getLogger(SpeakerRus.class); - - @Override - public String say() { - return "Привет"; - } - - public void init() { - log.info("SpeakerRus.init()"); - } - - public void destroy() { - log.info("SpeakerRus.destroy()"); - } -} diff --git a/demo/src/main/java/com/example/demo/speaker/service/SpeakerService.java b/demo/src/main/java/com/example/demo/speaker/service/SpeakerService.java deleted file mode 100644 index de3a463..0000000 --- a/demo/src/main/java/com/example/demo/speaker/service/SpeakerService.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.example.demo.speaker.service; - -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Service; - -import com.example.demo.speaker.domain.Speaker; - -@Service -public class SpeakerService { - private final ApplicationContext applicationContext; - - public SpeakerService(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - public String say(String name, String lang) { - @SuppressWarnings("null") - final Speaker speaker = (Speaker) applicationContext.getBean(lang); - return String.format("%s, %s!", speaker.say(), name); - } -} diff --git a/demo/src/main/java/com/example/demo/types/api/TypeController.java b/demo/src/main/java/com/example/demo/types/api/TypeController.java index 3ff424f..7298db7 100644 --- a/demo/src/main/java/com/example/demo/types/api/TypeController.java +++ b/demo/src/main/java/com/example/demo/types/api/TypeController.java @@ -47,7 +47,9 @@ public class TypeController { // Получить все элементы @GetMapping public List getAll() { - return typeService.getAll().stream().map(this::toDto).toList(); + return typeService.getAll().stream() + .map(this::toDto) + .toList(); } // Получить элемент по идентификатору @@ -64,7 +66,9 @@ public class TypeController { // Изменить элемент @PutMapping("/{id}") - public TypeDto update(@PathVariable(name = "id") Long id, @RequestBody @Valid TypeDto dto) { + public TypeDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid TypeDto dto) { return toDto(typeService.update(id, toEntity(dto))); } diff --git a/demo/src/main/java/com/example/demo/types/api/TypeDto.java b/demo/src/main/java/com/example/demo/types/api/TypeDto.java index 62f766b..8e18c63 100644 --- a/demo/src/main/java/com/example/demo/types/api/TypeDto.java +++ b/demo/src/main/java/com/example/demo/types/api/TypeDto.java @@ -3,6 +3,7 @@ package com.example.demo.types.api; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; // DTO для сущности "Тип" (Тип книги) public class TypeDto { @@ -12,6 +13,7 @@ public class TypeDto { // Название типа @NotBlank + @Size(min = 5, max = 50) private String name; // Получить идентификатор diff --git a/demo/src/main/java/com/example/demo/types/model/TypeEntity.java b/demo/src/main/java/com/example/demo/types/model/TypeEntity.java index 5b93660..28a7a8c 100644 --- a/demo/src/main/java/com/example/demo/types/model/TypeEntity.java +++ b/demo/src/main/java/com/example/demo/types/model/TypeEntity.java @@ -4,19 +4,24 @@ import java.util.Objects; import com.example.demo.core.model.BaseEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Column; + // Сущность "Тип" (Тип книги) +@Entity +@Table(name = "types") public class TypeEntity extends BaseEntity { // Название типа + @Column(nullable = false, unique = true, length = 50) private String name; // Конструктор по умолчанию public TypeEntity() { - super(); } // Конструктор с параметрами для создания объекта - public TypeEntity(Long id, String name) { - super(id); + public TypeEntity(String name) { this.name = name; } diff --git a/demo/src/main/java/com/example/demo/types/repository/TypeRepository.java b/demo/src/main/java/com/example/demo/types/repository/TypeRepository.java index d491aec..bc384e9 100644 --- a/demo/src/main/java/com/example/demo/types/repository/TypeRepository.java +++ b/demo/src/main/java/com/example/demo/types/repository/TypeRepository.java @@ -1,11 +1,13 @@ 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/demo/src/main/java/com/example/demo/types/service/TypeService.java b/demo/src/main/java/com/example/demo/types/service/TypeService.java index 07e6ebd..0bcdf21 100644 --- a/demo/src/main/java/com/example/demo/types/service/TypeService.java +++ b/demo/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; @@ -20,36 +21,56 @@ public class TypeService { this.repository = repository; } + // Проверка уникальности названия типа + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Type with name %s is already exists", name)); + } + } + // Получить все элементы + @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"); + } + checkName(entity.getName()); + return repository.save(entity); } // Изменить элемент + @Transactional public TypeEntity update(Long id, TypeEntity entity) { final TypeEntity existsEntity = get(id); + checkName(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; } // Удалить все элементы + @Transactional public void deleteAll() { repository.deleteAll(); } diff --git a/demo/src/main/java/com/example/demo/users/api/UserController.java b/demo/src/main/java/com/example/demo/users/api/UserController.java index 7a9600d..5ca31dd 100644 --- a/demo/src/main/java/com/example/demo/users/api/UserController.java +++ b/demo/src/main/java/com/example/demo/users/api/UserController.java @@ -47,7 +47,9 @@ public class UserController { // Получить все элементы @GetMapping public List getAll() { - return userService.getAll().stream().map(this::toDto).toList(); + return userService.getAll().stream() + .map(this::toDto) + .toList(); } // Получить элемент по идентификатору @@ -64,7 +66,9 @@ public class UserController { // Изменить элемент @PutMapping("/{id}") - public UserDto update(@PathVariable(name = "id") Long id, @RequestBody @Valid UserDto dto) { + public UserDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid UserDto dto) { return toDto(userService.update(id, toEntity(dto))); } diff --git a/demo/src/main/java/com/example/demo/users/api/UserDto.java b/demo/src/main/java/com/example/demo/users/api/UserDto.java index db60ffc..1db392b 100644 --- a/demo/src/main/java/com/example/demo/users/api/UserDto.java +++ b/demo/src/main/java/com/example/demo/users/api/UserDto.java @@ -3,6 +3,7 @@ package com.example.demo.users.api; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; // DTO для сущности "Пользователь" public class UserDto { @@ -12,10 +13,12 @@ public class UserDto { // Имя/логин пользователя @NotBlank + @Size(min = 3, max = 50) private String username; // Пароль пользователя @NotBlank + @Size(min = 8) private String password; // Электронный адрес почты пользователя diff --git a/demo/src/main/java/com/example/demo/users/model/UserEntity.java b/demo/src/main/java/com/example/demo/users/model/UserEntity.java index d32c51a..1d61629 100644 --- a/demo/src/main/java/com/example/demo/users/model/UserEntity.java +++ b/demo/src/main/java/com/example/demo/users/model/UserEntity.java @@ -1,28 +1,52 @@ package com.example.demo.users.model; import java.util.Objects; +import java.util.Set; +import java.util.HashSet; import com.example.demo.core.model.BaseEntity; +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.messages.model.MessageEntity; + +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Column; +import jakarta.persistence.OneToMany; +import jakarta.persistence.CascadeType; +import jakarta.persistence.OrderBy; // Сущность "Пользователь" +@Entity +@Table(name = "users") public class UserEntity extends BaseEntity { // Имя/логин пользователя + @Column(nullable = false, unique = true, length = 50) private String username; // Пароль пользователя + @Column(nullable = false, length = 50) private String password; // Электронный адрес почты пользователя + @Column(nullable = false, unique = true) private String email; + // Список заказов пользователя + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + @OrderBy("id ASC") + private Set orders = new HashSet<>(); + + // Список сообщений пользователя + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + @OrderBy("id ASC") + private Set messages = new HashSet<>(); + // Конструктор по умолчанию public UserEntity() { - super(); } // Конструктор с параметрами для создания объекта - public UserEntity(Long id, String username, String password, String email) { - super(id); + public UserEntity(String username, String password, String email) { this.username = username; this.password = password; this.email = email; @@ -58,10 +82,36 @@ public class UserEntity extends BaseEntity { this.email = email; } + // Получить список заказов + public Set getOrders() { + return orders; + } + + // Добавить заказ + public void addOrder(OrderEntity order) { + if (order.getUser() != this) { + order.setUser(this); + } + orders.add(order); + } + + // Получить список сообщений + public Set getMessages() { + return messages; + } + + // Добавить сообщение + public void addMessage(MessageEntity message) { + if (message.getUser() != this) { + message.setUser(this); + } + messages.add(message); + } + // Получить хэш-код объекта @Override public int hashCode() { - return Objects.hash(id, username, password, email); + return Objects.hash(id, username, password, email, orders, messages); } // Сравнить объекты @@ -75,6 +125,8 @@ public class UserEntity extends BaseEntity { return Objects.equals(other.getId(), id) && Objects.equals(other.getUsername(), username) && Objects.equals(other.getPassword(), password) - && Objects.equals(other.getEmail(), email); + && Objects.equals(other.getEmail(), email) + && Objects.equals(other.getOrders(), orders) + && Objects.equals(other.getMessages(), messages); } } diff --git a/demo/src/main/java/com/example/demo/users/repository/UserRepository.java b/demo/src/main/java/com/example/demo/users/repository/UserRepository.java index fcff015..14c8ae7 100644 --- a/demo/src/main/java/com/example/demo/users/repository/UserRepository.java +++ b/demo/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -1,11 +1,16 @@ package com.example.demo.users.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.users.model.UserEntity; // Хранилище для сущности "Пользователь" -@Repository -public class UserRepository extends MapRepository { +public interface UserRepository extends CrudRepository { + // Получить пользователя по имени/логину + Optional findByUsername(String username); + + // Получить пользователя по адресу электронной почты + Optional findByEmail(String email); } diff --git a/demo/src/main/java/com/example/demo/users/service/UserService.java b/demo/src/main/java/com/example/demo/users/service/UserService.java index 79e03af..fe3f8b5 100644 --- a/demo/src/main/java/com/example/demo/users/service/UserService.java +++ b/demo/src/main/java/com/example/demo/users/service/UserService.java @@ -1,9 +1,10 @@ package com.example.demo.users.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.users.model.UserEntity; @@ -20,38 +21,66 @@ public class UserService { this.repository = repository; } + // Проверка уникальности имени/логина пользователя + private void checkUsername(String username) { + if (repository.findByUsername(username).isPresent()) { + throw new IllegalArgumentException( + String.format("User with username %s is already exists", username)); + } + } + + // Проверка уникальности адреса электронной почты пользователя + private void checkEmail(String email) { + if (repository.findByEmail(email).isPresent()) { + throw new IllegalArgumentException( + String.format("User with email %s is already exists", email)); + } + } + // Получить все элементы + @Transactional(readOnly = true) public List getAll() { - return repository.getAll(); + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); } // Получить элемент по идентификатору + @Transactional(readOnly = true) public UserEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); } // Создать элемент + @Transactional public UserEntity create(UserEntity entity) { - return repository.create(entity); + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkUsername(entity.getUsername()); + checkEmail(entity.getEmail()); + return repository.save(entity); } // Изменить элемент + @Transactional public UserEntity update(Long id, UserEntity entity) { final UserEntity existsEntity = get(id); existsEntity.setUsername(entity.getUsername()); existsEntity.setPassword(entity.getPassword()); existsEntity.setEmail(entity.getEmail()); - return repository.update(existsEntity); + return repository.save(existsEntity); } // Удалить элемент + @Transactional public UserEntity delete(Long id) { final UserEntity existsEntity = get(id); - return repository.delete(existsEntity); + repository.delete(existsEntity); + return existsEntity; } // Удалить все элементы + @Transactional public void deleteAll() { repository.deleteAll(); } diff --git a/demo/src/main/resources/application.properties b/demo/src/main/resources/application.properties index 8b13789..9e9b0ee 100644 --- a/demo/src/main/resources/application.properties +++ b/demo/src/main/resources/application.properties @@ -1 +1,20 @@ +# Server +spring.main.banner-mode=off +server.port=8080 +# 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:file:./data +spring.datasource.username=factorino +spring.datasource.password=password +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.open-in-view=false +# spring.jpa.show-sql=true +# spring.jpa.properties.hibernate.format_sql=true + +# H2 console +spring.h2.console.enabled=true \ No newline at end of file diff --git a/demo/src/test/java/com/example/demo/ItemServiceTests.java b/demo/src/test/java/com/example/demo/ItemServiceTests.java deleted file mode 100644 index 78559c1..0000000 --- a/demo/src/test/java/com/example/demo/ItemServiceTests.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.example.demo; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.example.demo.core.error.NotFoundException; -import com.example.demo.items.model.ItemEntity; -import com.example.demo.items.service.ItemService; -import com.example.demo.types.model.TypeEntity; -import com.example.demo.types.service.TypeService; - -@SpringBootTest -@TestMethodOrder(OrderAnnotation.class) -public class ItemServiceTests { - @Autowired - private ItemService itemService; - - @Autowired - private TypeService typeService; - - @Test - void getTest() { - Assertions.assertThrows(NotFoundException.class, () -> itemService.get(0L)); - } - - @Test - @Order(1) - void createTest() { - final TypeEntity type = typeService.create(new TypeEntity(null, "Protection")); - itemService.create(new ItemEntity(null, type, 50.00, 20)); - itemService.create(new ItemEntity(null, type, 12.00, 3)); - final ItemEntity last = itemService.create(new ItemEntity(null, type, 15.00, 6)); - - Assertions.assertEquals(3, itemService.getAll(0L).size()); - Assertions.assertEquals(last, itemService.get(3L)); - } - - @Test - @Order(2) - void updateTest() { - final TypeEntity type = typeService.create(new TypeEntity(null, "Sharpness")); - final ItemEntity entity = itemService.get(3L); - final Double oldPrice = entity.getPrice(); - final ItemEntity newEntity = itemService.update(3L, new ItemEntity(null, type, 30.00, 6)); - - Assertions.assertEquals(3, itemService.getAll(0L).size()); - Assertions.assertEquals(newEntity, itemService.get(3L)); - Assertions.assertEquals(30.00, newEntity.getPrice()); - Assertions.assertNotEquals(oldPrice, newEntity.getPrice()); - } - - @Test - @Order(3) - void deleteTest() { - itemService.delete(3L); - Assertions.assertEquals(2, itemService.getAll(0L).size()); - Assertions.assertThrows(NotFoundException.class, () -> itemService.get(3L)); - - final TypeEntity type = typeService.create(new TypeEntity(null, "Protection")); - final ItemEntity newEntity = itemService.create(new ItemEntity(null, type, 50.00, 10)); - Assertions.assertEquals(3, itemService.getAll(0L).size()); - Assertions.assertEquals(4L, newEntity.getId()); - - typeService.deleteAll(); - itemService.deleteAll(); - } -} diff --git a/demo/src/test/java/com/example/demo/MessageServiceTests.java b/demo/src/test/java/com/example/demo/MessageServiceTests.java deleted file mode 100644 index eb2c6f7..0000000 --- a/demo/src/test/java/com/example/demo/MessageServiceTests.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.example.demo; - -import java.time.LocalDateTime; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.example.demo.core.error.NotFoundException; -import com.example.demo.messages.model.MessageEntity; -import com.example.demo.messages.service.MessageService; -import com.example.demo.users.model.UserEntity; -import com.example.demo.users.service.UserService; - -@SpringBootTest -@TestMethodOrder(OrderAnnotation.class) -public class MessageServiceTests { - @Autowired - private MessageService messageService; - - @Autowired - private UserService userService; - - @Test - void getTest() { - Assertions.assertThrows(NotFoundException.class, () -> messageService.get(0L)); - } - - @Test - @Order(1) - void createTest() { - final UserEntity sender = userService.create(new UserEntity(null, "User1", "password1", "mail1@gmail.com")); - messageService.create(new MessageEntity(null, sender, "Message1", LocalDateTime.now(), false)); - messageService.create(new MessageEntity(null, sender, "Message2", LocalDateTime.now(), false)); - final MessageEntity last = messageService.create(new MessageEntity(null, sender, "Message3", LocalDateTime.now(), false)); - - Assertions.assertEquals(3, messageService.getAll(0l).size()); - Assertions.assertEquals(last, messageService.get(3L)); - } - - @Test - @Order(2) - void updateTest() { - final UserEntity sender = userService.create(new UserEntity(null, "TESTuser", "password", "mail@gmail.com")); - final MessageEntity entity = messageService.get(3L); - final String oldText = entity.getText(); - final MessageEntity newEntity = messageService.update(3L, new MessageEntity(1L, sender, "textMessage", LocalDateTime.now(), false)); - - Assertions.assertEquals(3, messageService.getAll(0L).size()); - Assertions.assertEquals(newEntity, messageService.get(3L)); - Assertions.assertEquals("textMessage", newEntity.getText()); - Assertions.assertNotEquals(oldText, newEntity.getText()); - } - - @Test - @Order(3) - void deleteTest() { - messageService.delete(3L); - Assertions.assertEquals(2, messageService.getAll(0L).size()); - Assertions.assertThrows(NotFoundException.class, () -> messageService.get(3L)); - - final UserEntity sender = userService.create(new UserEntity(null, "User1", "password1", "mail1@gmail.com")); - final MessageEntity newEntity = messageService.create(new MessageEntity(null, sender, "Message", LocalDateTime.now(), false)); - Assertions.assertEquals(3, messageService.getAll(0L).size()); - Assertions.assertEquals(4L, newEntity.getId()); - - userService.deleteAll(); - messageService.deleteAll(); - } -} diff --git a/demo/src/test/java/com/example/demo/SpeakerServiceTests.java b/demo/src/test/java/com/example/demo/SpeakerServiceTests.java deleted file mode 100644 index cb43d1d..0000000 --- a/demo/src/test/java/com/example/demo/SpeakerServiceTests.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.example.demo; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.example.demo.speaker.service.SpeakerService; - -@SpringBootTest -class SpeakerServiceTests { - @Autowired - SpeakerService speakerService; - - @Test - void testSpeakerRus() { - final String res = speakerService.say("Мир", "ru"); - Assertions.assertEquals("Привет, Мир!", res); - } - - @Test - void testSpeakerEng() { - final String res = speakerService.say("World", "en"); - Assertions.assertEquals("Hello, World!", res); - } - - @Test - void testSpeakerDeu() { - final String res = speakerService.say("Welt", "de"); - Assertions.assertEquals("Hallo, Welt!", res); - } - - @Test - void testSpeakerErrorWired() { - Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> speakerService.say("Мир", "rus")); - } -} \ No newline at end of file diff --git a/demo/src/test/java/com/example/demo/TypeServiceTests.java b/demo/src/test/java/com/example/demo/TypeServiceTests.java index 7d22458..519c624 100644 --- a/demo/src/test/java/com/example/demo/TypeServiceTests.java +++ b/demo/src/test/java/com/example/demo/TypeServiceTests.java @@ -1,12 +1,14 @@ package com.example.demo; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; import com.example.demo.core.error.NotFoundException; import com.example.demo.types.model.TypeEntity; @@ -16,50 +18,65 @@ import com.example.demo.types.service.TypeService; @TestMethodOrder(OrderAnnotation.class) class TypeServiceTests { @Autowired - private TypeService typeService; + private TypeService typeService; + + private TypeEntity type; + + @BeforeEach + void createData() { + removeData(); + + type = typeService.create(new TypeEntity("Protection")); + typeService.create(new TypeEntity("Sharpness")); + typeService.create(new TypeEntity("Infinity")); + } + + @AfterEach + void removeData() { + typeService.deleteAll(); + } @Test - void getTest() { - Assertions.assertThrows(NotFoundException.class, () -> typeService.get(0L)); - } + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> typeService.get(0L)); + } + + @Test + void createTest() { + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertEquals(type, typeService.get(type.getId())); + } @Test - @Order(1) - void createTest() { - typeService.create(new TypeEntity(null, "Protection")); - typeService.create(new TypeEntity(null, "Sharpness")); - final TypeEntity last = typeService.create(new TypeEntity(null, "Infinity")); - - Assertions.assertEquals(3, typeService.getAll().size()); - Assertions.assertEquals(last, typeService.get(3L)); - } + void createNotUniqueTest() { + final TypeEntity nonUniqueType = new TypeEntity("Protection"); + Assertions.assertThrows(IllegalArgumentException.class, () -> typeService.create(nonUniqueType)); + } @Test - @Order(2) - void updateTest() { - final String test = "TEST"; - final TypeEntity entity = typeService.get(3L); - final String oldName = entity.getName(); - final TypeEntity newEntity = typeService.update(3L, new TypeEntity(1L, test)); - - Assertions.assertEquals(3, typeService.getAll().size()); - Assertions.assertEquals(newEntity, typeService.get(3L)); - Assertions.assertEquals(test, newEntity.getName()); - Assertions.assertNotEquals(oldName, newEntity.getName()); - } + void createNullableTest() { + final TypeEntity nullableType = new TypeEntity(null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> typeService.create(nullableType)); + } @Test - @Order(3) - void deleteTest() { - typeService.delete(3L); - Assertions.assertEquals(2, typeService.getAll().size()); - final TypeEntity last = typeService.get(2L); - Assertions.assertEquals(2L, last.getId()); + void updateTest() { + final String test = "TEST"; + final String oldName = type.getName(); + final TypeEntity newEntity = typeService.update(type.getId(), new TypeEntity(test)); + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertEquals(newEntity, typeService.get(type.getId())); + Assertions.assertEquals(test, newEntity.getName()); + Assertions.assertNotEquals(oldName, newEntity.getName()); + } - final TypeEntity newEntity = typeService.create(new TypeEntity(null, "Infinity")); - Assertions.assertEquals(3, typeService.getAll().size()); - Assertions.assertEquals(4L, newEntity.getId()); + @Test + void deleteTest() { + typeService.delete(type.getId()); + Assertions.assertEquals(2, typeService.getAll().size()); - typeService.deleteAll(); - } + final TypeEntity newEntity = typeService.create(new TypeEntity(type.getName())); + Assertions.assertEquals(3, typeService.getAll().size()); + Assertions.assertNotEquals(type.getId(), newEntity.getId()); + } } diff --git a/demo/src/test/java/com/example/demo/UserMessageServiceTests.java b/demo/src/test/java/com/example/demo/UserMessageServiceTests.java new file mode 100644 index 0000000..99c82de --- /dev/null +++ b/demo/src/test/java/com/example/demo/UserMessageServiceTests.java @@ -0,0 +1,90 @@ +package com.example.demo; + +import java.time.LocalDateTime; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.messages.model.MessageEntity; +import com.example.demo.messages.service.MessageService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.persistence.EntityManager; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class UserMessageServiceTests { + @Autowired + private EntityManager entityManager; + + @Autowired + private UserService userService; + + @Autowired + private MessageService messageService; + + + private UserEntity user1; + private UserEntity user2; + private UserEntity user3; + + @BeforeEach + void createData() { + removeData(); + + user1 = userService.create(new UserEntity("user1", "password", "mail1@gmail.com")); + user2 = userService.create(new UserEntity("user2", "password", "mail2@gmail.com")); + user3 = userService.create(new UserEntity("user3", "password", "mail3@gmail.com")); + + final var messages1 = List.of( + new MessageEntity("message1", LocalDateTime.now(), false), + new MessageEntity("message2", LocalDateTime.now(), false), + new MessageEntity("message3", LocalDateTime.now(), false) + ); + messages1.forEach(message -> messageService.create(user1.getId(), message)); + + final var messages2 = List.of( + new MessageEntity("Message4", LocalDateTime.now(), false), + new MessageEntity("Message5", LocalDateTime.now(), false) + ); + messages2.forEach(message -> messageService.create(user2.getId(), message)); + } + + @AfterEach + void removeData() { + userService.deleteAll(); + } + + @Test + @Order(1) + void createTest() { + Assertions.assertEquals(5, messageService.getAll(0L).size()); + } + + @Test + @Order(2) + void orderFilterTest() { + Assertions.assertEquals(3, messageService.getAll(user1.getId()).size()); + Assertions.assertEquals(2, messageService.getAll(user2.getId()).size()); + Assertions.assertEquals(0, messageService.getAll(user3.getId()).size()); + } + + @Test + @Order(3) + void userCascadeDeleteTest() { + userService.delete(user1.getId()); + final var messages = entityManager.createQuery( + "select count(o) from MessageEntity o where o.user.id = :userId"); + messages.setParameter("userId", user1.getId()); + Assertions.assertEquals(0, messages.getFirstResult()); + } +} diff --git a/demo/src/test/java/com/example/demo/UserOrderServiceTests.java b/demo/src/test/java/com/example/demo/UserOrderServiceTests.java new file mode 100644 index 0000000..484f377 --- /dev/null +++ b/demo/src/test/java/com/example/demo/UserOrderServiceTests.java @@ -0,0 +1,100 @@ +package com.example.demo; + +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.example.demo.orders.model.OrderEntity; +import com.example.demo.orders.service.OrderService; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +import jakarta.persistence.EntityManager; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class UserOrderServiceTests { + @Autowired + private EntityManager entityManager; + + @Autowired + private TypeService typeService; + + @Autowired + private UserService userService; + + @Autowired + private OrderService orderService; + + private TypeEntity type1; + private TypeEntity type2; + private TypeEntity type3; + + private UserEntity user1; + private UserEntity user2; + + @BeforeEach + void createData() { + removeData(); + + type1 = typeService.create(new TypeEntity("Protection")); + type2 = typeService.create(new TypeEntity("Sharpness")); + type3 = typeService.create(new TypeEntity("Infinity")); + + user1 = userService.create(new UserEntity("user1", "password", "mail1@gmail.com")); + user2 = userService.create(new UserEntity("user2", "password", "mail2@gmail.com")); + + final var orders = List.of( + new OrderEntity(type1, 12.00, 3), + new OrderEntity(type1, 50.00, 20), + new OrderEntity(type2, 15.00, 30), + new OrderEntity(type2, 64.00, 10), + new OrderEntity(type2, 15.00, 6), + new OrderEntity(type3, 80.00, 6), + new OrderEntity(type3, 64.00, 3) + ); + orders.forEach(order -> orderService.create(user1.getId(), order)); + } + + @AfterEach + void removeData() { + userService.deleteAll(); + typeService.deleteAll(); + } + + @Test + @Order(1) + void createTest() { + Assertions.assertEquals(7, orderService.getAll(user1.getId(), 0L).size()); + Assertions.assertEquals(0, orderService.getAll(user2.getId(), 0L).size()); + } + + @Test + @Order(2) + void orderFilterTest() { + Assertions.assertEquals(2, orderService.getAll(user1.getId(), type1.getId()).size()); + Assertions.assertEquals(3, orderService.getAll(user1.getId(), type2.getId()).size()); + Assertions.assertEquals(2, orderService.getAll(user1.getId(), type3.getId()).size()); + } + + @Test + @Order(3) + void userCascadeDeleteTest() { + userService.delete(user1.getId()); + final var orders = entityManager.createQuery( + "select count(o) from OrderEntity o where o.user.id = :userId"); + orders.setParameter("userId", user1.getId()); + Assertions.assertEquals(0, orders.getFirstResult()); + } +} diff --git a/demo/src/test/java/com/example/demo/UserServiceTests.java b/demo/src/test/java/com/example/demo/UserServiceTests.java deleted file mode 100644 index 1ddc9da..0000000 --- a/demo/src/test/java/com/example/demo/UserServiceTests.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.example.demo; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.example.demo.core.error.NotFoundException; -import com.example.demo.users.model.UserEntity; -import com.example.demo.users.service.UserService; - -@SpringBootTest -@TestMethodOrder(OrderAnnotation.class) -public class UserServiceTests { - @Autowired - private UserService userService; - - @Test - void getTest() { - Assertions.assertThrows(NotFoundException.class, () -> userService.get(0L)); - } - - @Test - @Order(1) - void createTest() { - userService.create(new UserEntity(null, "User1", "password1", "mail1@gmail.com")); - userService.create(new UserEntity(null, "User2", "password2", "mail2@gmail.com")); - final UserEntity last = userService.create(new UserEntity(null, "User3", "password3", "mail3@gmail.com")); - - Assertions.assertEquals(3, userService.getAll().size()); - Assertions.assertEquals(last, userService.get(3L)); - } - - @Test - @Order(2) - void updateTest() { - final String test = "TEST"; - final String password = "qwerty"; - final String email = "mail@xmail.ru"; - final UserEntity entity = userService.get(3L); - final String oldUsername = entity.getUsername(); - final UserEntity newEntity = userService.update(3L, new UserEntity(1L, test, password, email)); - - Assertions.assertEquals(3, userService.getAll().size()); - Assertions.assertEquals(newEntity, userService.get(3L)); - Assertions.assertEquals(test, newEntity.getUsername()); - Assertions.assertNotEquals(oldUsername, newEntity.getUsername()); - } - - @Test - @Order(3) - void deleteTest() { - userService.delete(3L); - Assertions.assertEquals(2, userService.getAll().size()); - final UserEntity last = userService.get(2L); - Assertions.assertEquals(2L, last.getId()); - - final UserEntity newEntity = userService.create(new UserEntity(null, "User4", "password4", "mail4@gmail.com")); - Assertions.assertEquals(3, userService.getAll().size()); - Assertions.assertEquals(4L, newEntity.getId()); - - userService.deleteAll(); - } -} diff --git a/demo/src/test/resources/application.properties b/demo/src/test/resources/application.properties new file mode 100644 index 0000000..eaabdf0 --- /dev/null +++ b/demo/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=factorino +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