2011-01-22 17 views
15

Używam sprawdzania poprawności Hibernate i chciałbym rozwiązać nazwę kategorii w komunikacie o błędzie. Rozważmy następujący prosty scenariusz:Jak dynamicznie rozwiązywać parametry wiadomości za pomocą walidatora Hibernate?

public class Category { 
    private String name; 
} 

public class Product { 
    @HazardousCategoryConstraint(message = "{haz.cat.error}") 
    private Category category; 
    private String name; 
} 

public class InventoryReport { 
    @Valid 
    private List<Product> products; 
} 


ValidationMessages.properties 
haz.cat.error={name} is a product in the hazardous category list. 

Załóżmy, że mam działającą implementację HazardousCategoryConstraint. Walidator sprawdza nazwę każdej kategorii przed listą zastrzeżonych nazw. Kiedy wołam sprawdzania poprawności (InventoryReport) otrzymuję liczbę błędów, których oczekuję, z wyjątkiem tego samego ciągu. Chciałbym, aby nazwa kategorii została przetłumaczona na każdą wiadomość. Czy ktoś może wskazać mi przykład dynamicznego rozdzielania parametrów lub pokazywania mi, jak to zrobić?

+0

Co poprosić o to: jak umieścić wartość w ciągu komunikat o błędzie? -- czy to jest poprawne? – Ralph

+0

@Ralph - to prawda, ale dokładniej chcę, żeby była dynamiczna. Mam wiele wystąpień kategorii o unikalnych nazwach. –

+0

Ale nazwy, które chcesz umieścić w wiadomości, są znane walidatorowi, prawda? – Ralph

Odpowiedz

10

IMO, proste rozwiązanie polega na stworzeniu niestandardowej implementacji javax.validation.MessageInterpolator. Przekaż główne zadanie do sprawdzania poprawności w Hibernate Validator ResourceBundleMessageInterpolator i wykonaj wymagane prace zastępcze w CustomMessageInterpolator.

public class CustomMessageInterpolator extends org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator { 

    private static final Pattern MESSAGE_PARAMETER_PATTERN = Pattern.compile("(\\{[^\\}]+?\\})"); 

    @Override 
    public String interpolate(String message, Context context) { 
     String resolvedMessage = super.interpolate(message, context); 
     resolvedMessage = replacePropertyNameWithPropertyValues(resolvedMessage, context.getValidatedValue()); 
     return resolvedMessage; 
    } 

    private String replacePropertyNameWithPropertyValues(String resolvedMessage, Object validatedValue) { 
     Matcher matcher = MESSAGE_PARAMETER_PATTERN.matcher(resolvedMessage); 
     StringBuffer sb = new StringBuffer(); 

     while (matcher.find()) { 
      String parameter = matcher.group(1); 

      String propertyName = parameter.replace("{", ""); 
      propertyName = propertyName.replace("}", ""); 

      PropertyDescriptor desc = null; 
      try { 
       desc = new PropertyDescriptor(propertyName, validatedValue.getClass()); 
      } catch (IntrospectionException ignore) { 
       matcher.appendReplacement(sb, parameter); 
       continue; 
      } 

      try { 
       Object propertyValue = desc.getReadMethod().invoke(validatedValue); 
       matcher.appendReplacement(sb, propertyValue.toString()); 
      } catch (Exception ignore) { 
       matcher.appendReplacement(sb, parameter); 
      } 
     } 
     matcher.appendTail(sb); 
     return sb.toString(); 
    } 

} 

@Test

public void validate() { 
     Configuration<?> configuration = Validation.byDefaultProvider().configure(); 
     ValidatorFactory validatorFactory = configuration.messageInterpolator(new CustomMessageInterpolator()).buildValidatorFactory(); 
     Validator validator = validatorFactory.getValidator(); 

     Product p = new Product(); 
     Category cat = new Category(); 
     cat.setName("s"); //assume specified name is invalid 
     p.setCategory(cat); 

     Set<ConstraintViolation<Product>> violations = validator.validate(p); 
     for(ConstraintViolation<Product> violation : violations) { 
      System.out.println(violation.getMessage()); 
     } 
    } 

Wyjście

s is a product in the hazardous category list. 
+0

Jak ustawić niestandardowy interpolator komunikatów dla stałego używania JPA? – alexsmail

+0

Po prostu sprawa OO: Zamiast rozszerzania 'ResourceBundleMessageInterpolator 'Hibernate Validator wolałbym użyć delegata w' CustomMessageInterpolator', aby był niezależny od wewnętrznych elementów Hibernate. –

Powiązane problemy