Tag Archives: hibernate

Walidacja identyczności haseł – Hibernate Validator, Spring Framework

Natknąłem się na problem walidacji identyczności haseł w formularzu rejestracji użytkownika.
Korzystam ze Spring Frameworka, a do walidacji wykorzystuję bibliotekę Hibernate Validator w wersji 4.

Pierwszym problemem okazał się brak pola confirmPassword  w klasie User. Poszperałem na forach i znalazłem w rozwiązania tego problemu:

  1. Stworzyć dodatkowe pole w klasie User  – confirmPassword
  2. Stworzyć dodatkową klasę, przypuśćmy CreateUserForm, mniej więcej tak:
class CreateUserForm {
String confirmPassword;
User user;
(... )
}

Rozwiązanie to wydaje mi się “ładniejsze” niż pierwsze, gdyż nie tworzymy w klasie, wykorzystywanej przy każdym requescie pola, które w ogóle nam jest niepotrzebne.

Kolejnym problemem okazała się walidacja. Chcieliśmy w 100% korzystać z funkcjonalności, jakie dają nam adnotacje z Hibernate Validatora. Brakuje jednak tam adnotacji umożliwiającej porównywanie ze sobą 2 pól.

Tutaj też skorzystałem z forów. Jedna osoba z teamu hibernate poleciła mi wykorzystanie constraintów (przepraszam za nieprzetłumaczenie :)) przypisanych do klas i zmianę domyślnego błędu zwracanego przez ten walidator.

Poniżej przedstawię rozwiązanie od jakiego doszedłem:

Poniżej znajduje się definicja interfejsu odpowiedzialnego za adnotację @SamePassword

@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = SamePasswordValidator.class)
public @interface SamePassword {
String message() default "{pl.aetas.gamestore.validator.constraint.SamePassword}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

Sama klasa walidatora wygląda tak:

public class SamePasswordValidator implements ConstraintValidator<SamePassword, Object> {
SamePassword constraintAnnotation;
public boolean isValid(Object value, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(constraintAnnotation.message()).addNode("confirmPassword").addConstraintViolation();
CreateUserForm u = (CreateUserForm) value;
if (u.getConfirmPassword().equals(u.getUser().getPassword())) {
return true;
}
return false;
}
public void initialize(SamePassword constraintAnnotation) {
this.constraintAnnotation = constraintAnnotation;
}
}

Aby skorzystać z w/w rozwiązania wystarczy w klasie CreateUserForm dodać adnotację @SamePassword (na poziomie klasy) i .. tyle 🙂

Mam nadzieję, że komuś się to przyda. W naszym projekcie adnotacje znacznie zwiększyły czytelność kodu, a jest to bardzo istotne przy pracy grupowej.

Java – Spring 2.5.6 testowanie DAO z użyciem adnotacji

Ostatnimi czasy siedzę trochę w Spring Framework, frameworku dla Javy. Doszedłem do etapu testów i stwierdziłem, że godnym opisania będzie sposób testowania metod opartych o transakcje, gdyż ciekawym jest fakt iż wszystko co odbywa się w danych testach (dodawanie danych, edycja itd.) jest następnie cofane do wersji pierwotnej (wywoływany jest rollback na transakcji) i dzieje się to automatycznie.

Poniżej znajduje się przykładowa klasa wraz z metodami testującymi. Wszystko oparte jest na adnotacjach, więc nie ma problemu:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:test-config.xml"
public class UserAccountDaoImplTest {
    @Resource
    UserAccountDaoImpl userAccountDaoImpl;
    @Test
    @Transactional
    public void testSaveUser() {
        System.out.println("saveUser");
        UserAccount transientInstance = new UserAccount();
        transientInstance.setEmail("email@mail.con");
        transientInstance.setEnabled(true);
        userAccountDaoImpl.saveUser(transientInstance);
        UserAccount userFromDb = userAccountDaoImpl.getByEmail("email@mail.con);
        assertNotNull("User should be not null", userFromDb);
assertTrue("User should be enabled", userFromDb.isEnabled());
        assertTrue("User should be non locked", userFromDb.isAccountNonLocked());
    }
    @Test(expected = ConstraintViolationException.class)
    @Transactional
    public void testSaveUserDuplicateEmail() {
        System.out.println("saveUserDuplicateEmail");
        UserAccount transientInstance = new UserAccount();
        transientInstance.setEmail("michal@somedomain.pl");
        userAccountDaoImpl.saveUser(transientInstance);
    }
    @Test
    @NotTransactional
    public void testGetUserById() {
        System.out.println("getUserById");
        long id = 1L;
        UserAccount result = userAccountDaoImpl.getUserById(id);
        assertNotNull("User with id " + id + " should be found", result);
        assertEquals(new Long(id), result.getId())        id = 667L;
        result = userAccountDaoImpl.getUserById(id);
        assertNull("User with id " + id + " should not be found", result);
    @Test(expected = UsernameNotFoundException.class)
    @NotTransactional
    public void testGetByEmailNoUser() {
        System.out.println("getByEmailNoUser");
        String email = "someone@gnail.con";
        userAccountDaoImpl.getByEmail(email);
    }
}

Początkowo inicjujemy kontekst springa (definiujemy która klasa się tym zajmie i dodajemy plik konfiguracyjny).

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = “classpath:test-config.xml”)

Następnie musimy wstrzyknąć do testu instancję którą testujemy, wykorzystujemy do tego adnotację @Resource:

@Resource
UserAccountDaoImpl userAccountDaoImpl;

Nie będę tutaj opisywał adnotacji czystego JUnita, zwrócę jedynie uwagę na adnotacje potrzebne przy testowaniu danych opartych o DB. Tak więc mamy:

@Transactional – określa nam, że dana metoda korzysta z transakcji i ma być wykonany rollback
@NotTransactional – odwrotnie do tego wyżej 🙂

Dodatkowo, co nie zostało pokazane na powyższym przykładzie, możemy skorzystać z adnotacji @Rollback(false) jeżeli nie chcemy, aby wykonywany był rollback po wykonaniu testu.

Mam nadzieję, że powyższy (działający u mnie) przykład komuś się przyda. Ja chwilę spędziłem, zanim doprowadziłem go do porządku, choć okazało się to niezwykle proste (wynik końcowy) 🙂