From ae8697c3158ed6e87a389afdeb4c4bb9ebcf97e1 Mon Sep 17 00:00:00 2001 From: stvort Date: Sat, 11 May 2024 20:02:03 +0400 Subject: [PATCH] jpa template added --- .../ru/otus/hw/models/ModelsCommonTest.java | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 templates/hw06-jpa/src/test/java/ru/otus/hw/models/ModelsCommonTest.java diff --git a/templates/hw06-jpa/src/test/java/ru/otus/hw/models/ModelsCommonTest.java b/templates/hw06-jpa/src/test/java/ru/otus/hw/models/ModelsCommonTest.java new file mode 100644 index 00000000..9ab54995 --- /dev/null +++ b/templates/hw06-jpa/src/test/java/ru/otus/hw/models/ModelsCommonTest.java @@ -0,0 +1,132 @@ +package ru.otus.hw.models; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.OneToOne; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.reflections.ReflectionUtils; +import org.reflections.Reflections; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Objects.isNull; +import static org.assertj.core.api.Assertions.assertThat; + +class ModelsCommonTest { + + private static Set> entitiesClasses; + + @BeforeAll + static void setUpAll() { + /* + Чтобы работало подключить + + 0.10.2 + + + + org.reflections + reflections + ${reflections.version} + + */ + var reflections = new Reflections("ru.otus.hw.models"); + entitiesClasses = reflections.getTypesAnnotatedWith(Entity.class); + + } + + @ParameterizedTest + @MethodSource("getEntities") + void shouldBeNoOneToOneRelationshipsInModelClasses(Class entityClass) { + + var oneToOneRelationshipExists = Arrays.stream(entityClass.getDeclaredFields()) + .anyMatch(f -> f.isAnnotationPresent(OneToOne.class)); + assertThat(oneToOneRelationshipExists) + .withFailMessage("В доменной модели ДЗ не предусмотрены связи OneToOne") + .isFalse(); + } + + @ParameterizedTest + @MethodSource("getEntities") + void shouldBeNoEagerRelationshipsInModelClasses(Class entityClass) { + boolean eagerFetchExists = Arrays.stream(entityClass.getDeclaredFields()) + .map(f -> getRelationAnnotationArgumentValue(f, "fetch", FetchType.class)) + .filter(Objects::nonNull) + .anyMatch(fetchType -> fetchType.equals(FetchType.EAGER)); + assertThat(eagerFetchExists) + .withFailMessage("Лучше все связи сделать LAZY") + .isFalse(); + } + + @ParameterizedTest + @MethodSource("getEntities") + void shouldMappedForBidirectionalRelationshipsInModelClasses(Class entityClass) { + var relationsEntries = findAllRelationsEntry(entityClass); + var hasBidirectionalRelationshipsWithoutMappedBy = relationsEntries.entrySet().stream() + .anyMatch(relationEntry -> { + var reverseRelations = findAllRelationsEntry(relationEntry.getKey()); + var reverseRelationField = reverseRelations.get(entityClass); + if (isNull(reverseRelationField)) { + return false; + } + var relationFieldName = relationEntry.getValue().getName(); + var reverseRelationFieldName = reverseRelationField.getName(); + var mappedByValue = getRelationAnnotationArgumentValue(relationEntry.getValue(), + "mappedBy", String.class); + var reverseMappedByValue = getRelationAnnotationArgumentValue(reverseRelationField, + "mappedBy", String.class); + + return !reverseRelationFieldName.equals(mappedByValue) && + !relationFieldName.equals(reverseMappedByValue); + }); + assertThat(hasBidirectionalRelationshipsWithoutMappedBy) + .withFailMessage("Двунаправленные связи должны быть настроены с помощью mappedBy") + .isFalse(); + + } + + private static Stream getEntities() { + return entitiesClasses.stream().map(Arguments::of); + } + + private T getRelationAnnotationArgumentValue(Field field, String argumentName, Class returnType) { + return Arrays.stream(field.getAnnotations()) + .flatMap(a -> Arrays.stream(a.getClass().getDeclaredMethods()).map(m -> Map.entry(m, a))) + .filter(e -> e.getKey().getName().equals(argumentName)) + .map(e -> ReflectionUtils.invoke(e.getKey(), e.getValue())) + .map(returnType::cast) + .findFirst().orElse(null); + } + + private Map, Field> findAllRelationsEntry(Class entityClass) { + return Arrays.stream(entityClass.getDeclaredFields()) + .filter(f -> !f.getType().isPrimitive()) + .map(f -> Map.entry(f, fieldToClass(f))) + .filter(e -> entitiesClasses.contains(e.getValue())) + .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + } + + private Class fieldToClass(Field field) { + var className = field.getType().getName(); + if (Collection.class.isAssignableFrom(field.getType())) { + className = field.getGenericType().getTypeName() + .split("<")[1].split(">")[0]; + + } + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file