Могут ли Eclipse или Spring Boot помочь мне найти методы, для которых требуются транзакции при отключении функции «открыть в представлении» в приложении весенней загрузки? - PullRequest
0 голосов
/ 24 сентября 2019

У меня есть довольно новое Java-приложение Spring Boot, над которым я работаю, но я не осознавал, что по умолчанию включена настройка open-in-view для jpa-модуля.

У меня были некоторые проблемыгде отключение этого параметра оказалось лучшим решением, но теперь я столкнулся с парой случаев, когда я случайно полагался на это.

Например, у меня был класс Service, который не был помечен как @Transactions,зацикливание на инициализируемом с отложенным исходным кодом дочернем элементе одной из моих сущностей, прежде чем она вернулась в контроллер.

Теперь эта функция генерирует исключение LazyInitializationException, как и ожидалось, поскольку я не нахожусь в транзакции.

Я еще не написал тестовые примеры, обеспечивающие 100% покрытие, есть ли что-то встроенное в Eclipse, или Sprint Tools Suite, или во время выполнения Spring Boot, о котором я могу сгенерировать какой-нибудь отчет, чтобы сообщить мне, когда классы, аннотированные с помощью @Entity, имеют функциювызовы, сделанные на них в вызовах нетранзакционных методов?

Любой другой способ попытаться идентифицироватьэто, не добавляя 100% тестов покрытия кода в мое приложение, или просматривая каждый вызов метода с помощью зубчатого гребня?

Если нет, то я бы работал над тестовыми примерами.

Спасибо за любые рекомендации.

1 Ответ

0 голосов
/ 26 сентября 2019

Это не идеально, но я нашел способ найти большинство случаев для моего приложения, используя библиотеку Reflections.Я запустил это как @SpringBootTest контрольный пример и поочередно исправляю ошибки.

@Test
public void testOne() throws NoSuchMethodException, SecurityException{
    Reflections reflections = new Reflections("my.base.package",
                        new SubTypesScanner(), 
                          new TypeAnnotationsScanner(),
                          new MemberUsageScanner());

    Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(javax.persistence.Entity.class);

    for( Class<?> c: annotated){
        log.debug(c.getSimpleName());
        for( Method m: c.getMethods()){
            if( m.getReturnType().isAnnotationPresent(Entity.class) ){
                checkTransactional(reflections,c, m, 0);
            }
        }
    }
   Assert.assertTrue(true);
}

public static void checkTransactional(Reflections reflections, Class<?> c, Method m, int depth) throws NoSuchMethodException, SecurityException{
    if( depth >64){
        throw new RuntimeException("No Transactional Method Found");
        //return;
    }
    String s = "--";
    Annotation t = null;

    Class<?>[] possibleAnnotations = {
            org.springframework.transaction.annotation.Transactional.class,
            javax.transaction.Transactional.class, 
    };

    Annotation[] annotations = m.getAnnotations();
    for( Annotation a: annotations){
        Class<?> aClass = a.annotationType();
        //// handle javax and spring Transactional
        if( aClass.getSimpleName().equals("Transactional")){
            t = a;
            s = "++";
            break;
        }
    }


    String prefix = "";
    for( int i = 0; i < depth; i++){
        prefix = prefix + s;
    }

    log.debug( prefix + " " + c.getCanonicalName() + ":" + m.getName());

    if( t != null ){
        log.trace("This is transactional");
        return;
    }

    Set<Member> callingMembers = reflections.getMethodUsage(m);
    if( callingMembers.size() == 0){
        log.error("Nothing calls this method, if it is a controller, we have a problem" + (c.isAnnotationPresent(Controller.class) || c.isAnnotationPresent(RestController.class)));
        if( (c.isAnnotationPresent(Controller.class) || c.isAnnotationPresent(RestController.class)) ){
            throw new RuntimeException("No transactional method found in call heirrchy before controller"); 
        }
    }
    for( Member usingMember: callingMembers){
        Class<?> callingClass = usingMember.getDeclaringClass();
        List<Method> callingMethods = new ArrayList<Method>();
        if( callingClass != c ){
            for( Method callingClassMethod: callingClass.getMethods()){
                if( callingClassMethod.getName().equals(usingMember.getName())){
                    callingMethods.add(callingClassMethod);
                }
            }   
        }
        if( callingMethods.size() > 2){
            log.error("Two methods call this, manually review");
            for( Method caller: callingMethods){
                log.debug( prefix + "!! " + c.getCanonicalName() + ":" + caller.getName() + ":" + caller.getParameterCount());
            }
            throw new RuntimeException("Two methods with same name ( overloading ) call this, manually review"); 

        }
        for( Method caller: callingMethods){
            checkTransactional(reflections, callingClass, caller, depth+1);
        }
    }
}
...