NullPointer podczas wstrzykiwania ziarenek do ContraintValidator'a

1

Tak jak w temacie, mam sobie klase implementującą ConstraintValidator<JakasAdnotacja, String>

public class JakasAdnotacjaValidator implements ConstraintValidator<JakasAdnotacja, String> {
    @Autowired
    private JakisService jakisService;

    @Override
    public void initialize(JakasAdnotacja jakasAdnotacja) {

    }

    @Override
    public boolean isValid(String costam, ConstraintValidatorContext constraintValidatorContext) {
        return jakisService.zrobCosIZwrocTrueFalse();
    }
}

I kiedy uruchamiam aplikacje i nastepuje wywołanie adnotacji przy walidacji dostaje coś mniej wiecej takiego (same początki stacktrace podałem, bo wyjatkow jest pare i dość duzo tego)

java.lang.NullPointerException
	com.reportme.model.validation.UsernameAvailableValidator.isValid(UsernameAvailableValidator.java:22)
	com.reportme.model.validation.UsernameAvailableValidator.isValid(UsernameAvailableValidator.java:10)
	org.hibernate.validator.internal.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:308)
	org.hibernate.validator.internal.engine.ConstraintTree.validateConstraints(ConstraintTree.java:180)
	org.hibernate.validator.internal.engine.ConstraintTree.validateConstraints(ConstraintTree.java:124)


javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
	org.hibernate.validator.internal.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:311)
	org.hibernate.validator.internal.engine.ConstraintTree.validateConstraints(ConstraintTree.java:180)
	org.hibernate.validator.internal.engine.ConstraintTree.validateConstraints(ConstraintTree.java:124)
	org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:85)
	org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:463)


javax.persistence.RollbackException: Error while committing the transaction
	org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:94)
	org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
	org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)


org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
	org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:526)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
	org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)

Wg tego co szukałem w google musiałem stworzyć bean:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

Po to zeby można było wstrzykiwać Validator gdzieś, ALE dodatkowo konfiguruje się SpringConstraintValidatorFactory - więc klasy implementujace ConstraintValidator staja sie beanem - więc moge do nich wstrzykiwać np serwisy. Niestety to nie podziałało.
Próbowałem też dodać adnotacje @Component - też nie podziałało

Kolejne podejście to ApplicationContextAware, próbowałem to implementować i szukać manualnie beana z kontekstu, ale też rzucało wyjątkiem (nie pamiętam już jakim, w razie czego przekoduje troche i powiem, jesli to bedzie potrzebne - liczę, że robie jakiś głupi błąd)

janusz pomusz..

edit
jeśli by ufać docs'om springa, problem leży gdzieś po stronie transakcji.. choć jak wywołam na jakisService metode która nie używa bazy, coś w stylu

public void doNothing(){
    System.out.println("napis");
}

to dalej mam nullpointerexception na tym service

0

Odpisze bo temat stanie się za długi
odkryłem, że to nie kwestia NullPointera w serwisie, a tego, że w kontrolerze jak mam cos w stylu:
if (są błędy) pokaż strone z błędami else zapisz entity przez wstrzyknięty @Service i pokaż inna strone

To jak w tym ConstraintValidatorze wstrzykne sobie ten sam service co w kontrolerze, ale go nie użyje, to jest ok. Dopiero jak w kontrolerze mam zapis entity i użyje serwisu w validatorze to rzuca tymi wyjątkami - i tu chodzi o coś z transakcjami zapewne

A jeśli chodzi o wstrzykiwanie - wiem, że działa poprawnie bo jak usune dodawanie entity z kontrolera to validacja (nawet jak użyje tego service) przebiega pomyślnie.
btw. W kontrolerze i tym validatorze uzywam tych samych service, gwoli wątpliwości

Czyli wygląda to tak:

  1. Jak w kontrolerze mam zapis entity i w validatorze używam tego service w metodzie isValid -> rzuca wyjatkiem
  2. Jak nie będe zapisywał entity, a validator zostawie tak jak jest (z użyciem service) to zvaliduje i wykona metode z serwisu - działa dobrze
  3. I vice versa -> jak bede zapisywał entity, ale usune adnotacje z pola w entity (tzn validator nie bedzie uzywany) to też działa.

Czyli problem leży gdzieś przy zapisie entity do bazy zaraz po tym, jak zostało @Valid'owane przez tego Validatora (gdzie używał tego samego serwisu co kontroler)
Może transakcje sa nie zamkniete? coś tam czemuś przeszkadza i nie bardzo wiem o co zahaczyć

ostrzegam, jak mi nikt nie odpowie zwołam 4programmersowe kółko javowe @niezdecydowany :D :D

0

Troszkę za mało danych dla mnie, ale strzelam, że powinieneś w @Transactional na serwisie ustawić REQUIRES_NEW.

0

Może błąd w konfiguracji ? Gdyby przy wstrzykiwaniu było coś nie halo to kontekst by nie wstawał. Zapodaj klasy / XMLki z configiem. Za długo Springiem sie nie bawię, ale już go lofciam <3

0

Konfigi raczej mam dobrze, bo osobno te operacje działają, ale razem nie. Coś musi przeszkadzać czemuś innemu, pytanie co..

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <mvc:annotation-driven validator="validator"/>
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <bean id="persistenceExceptionTranslationPostProcessor"
            class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor">
    </bean>

    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</beans>

Taki mały wycinek z tego co moze byc istotne. Mysle ze to nie kwestia Springa, a JPA

@niezdecydowany
http://pastebin.com/FTjD5gRR

edit: z tym, że stacktrace wyswietla sie w przegladarce, ale nie w konsoli zaraz po uruchomieniu tomcata

0

ten post to troche jak notatki z dzienniczka, no ale..
zbawienne okazało się w metodzie isValid sprawdzenie czy service nie jest nullem

System.err.println("przed ifem usernamevalidator");
        if(jakisService == null) {
            return true;
        }
        System.err.println("po ifie usernamevalidator");
        return jakisService.jakasMetoda(username);

O dziwo działa, wszystko tak jak powinno. Dałem sobie println jak wyżej + metode PostConstruct w serwisie żeby szybko zobaczyc co sie dzieje miejdzy SQLami hibernatowymi, a validacja, kiedy tworzy się serwis itp.

I dziwnie to wyglada, bo bean tworzy sie podczas uruchomienia aplikacji i podczas pierwszego uzycia aplikacji (jak tworzy się baza)-> więc już nullem nie jest, jak submituje formularz na stronie -> nie jest nullem i normalnie używa serwisu
dopiero pozniej jak mam SQL który dodaje entity do bazy** to jest wywołanie tej metody isValid poraz kolejny (przez co? dlaczego?) i tutaj już service jest nullem bo wyrzuca mi true (na konsoli pojawia się tylko napis "przed ifem usernamevalidator" więc jakisService=null)

** dla jasności: najpierw jest bindowanie i validacja danych a dopiero po sukcesie validacji jest zapis entity do bazy
Niesamowicie ciekawi mnie czemu tak jest..

0

Odpowiedź zawsze jest taka sama: obiektów jest więcej niż myślisz. Masz te swoje beany ze wstrzykniętymi zależnościami, ale masz też obiekty tworzone POZA kontenerem i one maja nulle. Chcesz wiedzieć skad? Zapnij debuger w konstruktorze i zobaczysz.

1 użytkowników online, w tym zalogowanych: 0, gości: 1