O padrão de uso do Validator do VRaptor seria assim:
validator.validate(user);
validator.onErrorForwardTo(this).signup();
service.insert(user);
Acontece que alguns atributos da minha classe são gerados automaticamente, como por exemplo signupDate e a senha (hash gerado com tamanho fixo). Minha classe User está assim:
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Column(unique = true)
@NotNull(message = "{LOGIN_CANNOT_BE_EMPTY}")
@Size(min = 4, max = 50)
@Pattern(regexp = "^(?=.{4,50}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$", message = "Nome de usuário inválido")
private String loginId;
@NotNull(message = "{NAME_CANNOT_BE_EMPTY}")
@Size(min = 4, max = 150)
private String name;
/**
* User's password encrypted (SHA-256)
*/
@NotNull(message = "{PASSWORD_CANNOT_BE_EMPTY}")
@Size(min = 71, max = 71)
private String password;
@NotNull
@Temporal(TemporalType.TIMESTAMP)
private Calendar signupDate;
Esses atributos automáticos são carregados dentro do EJB UserService.
@Override
@Transactional(TxType.REQUIRED)
public void insert(User entity) {
entity.setSignupDate(Calendar.getInstance());
entity.encryptPassword();
entityManager.persist(entity);
}
Quando eu usava o plugin vraptor-jpa cada requisição gerava uma transação com o banco de dados. Migrei do Tomcat para o Wildfly e estou usando JTA. Removi o plugin vraptor-jpa e estou controlando as transações via anotação @Transactional.
Eu fazia assim antes:
@Transactional
try {
service.insert(user);
//Validação do bean após carregamento dos atributos pelo serviço
validator.validate(user);
validator.onErrorForwardTo(this).signup();
service.flush();
}
No entanto isso ficou estranho. O controller não deveria depender da camada serviço para fazer uma validação. Se eu remover a anotação @Transactional do método do controller e remover o service.flush(), recebo o seguinte erro:
20:14:02,363 WARN [com.arjuna.ats.arjuna] (default task-32) ARJUNA012125: TwoPhaseCoordinator.beforeCompletion - failed for SynchronizationImple< 0:ffffc0a80166:12a2803d:5692cfb1:164, org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization@340e8f93 >: javax.persistence.PersistenceException: error during managed flush [...] Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [aaqd.model.User] during persist time for groups [javax.validation.groups.Default, ] List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='{PASSWORD_CANNOT_BE_EMPTY}', propertyPath=password, rootBeanClass=class aaqd.model.User, messageTemplate='{PASSWORD_CANNOT_BE_EMPTY}'} ConstraintViolationImpl{interpolatedMessage='{NAME_CANNOT_BE_EMPTY}', propertyPath=name, rootBeanClass=class aaqd.model.User, messageTemplate='{NAME_CANNOT_BE_EMPTY}'} ConstraintViolationImpl{interpolatedMessage='{LOGIN_CANNOT_BE_EMPTY}', propertyPath=loginId, rootBeanClass=class aaqd.model.User, messageTemplate='{LOGIN_CANNOT_BE_EMPTY}'} ]
Parece que o VRaptor não consegue capturar automaticamente as validações dessa exceção e jogar no Validator. Achei que isso era um problema, mas pensando melhor está certo. O Validator, do Controller, não deveria ser tão dependente da camada de serviço (que poderia estar rodando em um servidor EJB separado, por exemplo).
Eu testei isso e funcionou:
try {
if (service.getByLoginId(user.getLoginId()) != null) {
throw new UniqueConstraintViolatedException("loginId", "USER_ALREADY_EXISTS");
}
service.insert(user);
} catch (UniqueConstraintViolatedException e) {
validator.add(e.getValidationMessage());
} catch (Exception e) {
validator.validate(user);
if (!validator.hasErrors()) {
validator.add(
new SimpleMessage("bar", "Não foi possível realizar o cadastro (" + e.getMessage() + ")."));
e.printStackTrace();
}
}
No entanto, não sei se é a melhor alternativa. Alguém tem sugestões?
A anotação @NotNull para signupDate está correta? Penso que sim, tanto que na inserção dos dados é feita a validação. Um parâmetro nessa anotação resolveria o problema: @NotNull(validateOnView=“false”).
O que vocês acham? Eu estou muito errado?