验证程序抛出异常时,将禁止Bean验证。我想知道处理这个问题的正确方法是什么。表格:
<h:form id="registration_form">
<h:panelGrid columns="3">
<h:outputLabel for="username">Username</h:outputLabel>
<h:inputText id="username" value="#{userController.user.username}">
<f:validator binding="#{userController$UniqueUsernameValidator}"
redisplay="true"/> <!-- Not sure about this -->
<f:ajax event="blur" render="usernameMessage" />
</h:inputText>
<h:message id="usernameMessage" for="username" />
<!-- etc-->
</h:panelGrid>
</h:form>
UserController:
@ManagedBean
@ViewScoped
public class UserController {
// http://stackoverflow.com/a/10691832/281545
private User user;
@EJB
// do not inject stateful beans !
private UserService service;
public User getUser() {
return user;
}
@PostConstruct
void init() {
// http://stackoverflow.com/questions/3406555/why-use-postconstruct
user = new User();
}
@ManagedBean
@RequestScoped
public static class UniqueUsernameValidator implements Validator {
// Can't use a Validator (no injection) - see:
// http://stackoverflow.com/a/7572413/281545
@EJB
private UserService service;
@Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
if (value == null) return; // Let required="true" handle, if any.
try {
if (!service.isUsernameUnique((String) value)) {
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Username is already in use.", null));
}
} catch (Exception e) {
System.out.println(cause(e));
Throwable cause = e.getCause();
if (cause instanceof PersistenceException) {
Throwable cause2 = cause.getCause();
// ((PersistenceException)cause) - only superclass methods
if (cause2 instanceof DatabaseException) {
// now this I call ugly
int errorCode = ((DatabaseException) cause2)
.getDatabaseErrorCode(); // no java doc in eclipse
if (errorCode == 1406)
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Max 45 chars",
null));
}
}
// TODO: DEGUG, btw the EJBException has null msg
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, cause.getMessage(), null));
}
}
private static String cause(Exception e) {
StringBuilder sb = new StringBuilder("--->\nEXCEPTION:::::MSG\n"
+ "=================\n");
for (Throwable t = e; t != null; t = t.getCause())
sb.append(t.getClass().getSimpleName()).append(":::::")
.append(t.getMessage()).append("\n");
sb.append("FIN\n\n");
return sb.toString();
}
}
}
实体:
/** The persistent class for the users database table. */
@Entity
@Table(name = "users")
@NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private int iduser;
@NotNull(message = "Please enter a username")
@Pattern(regexp = "[A-Za-z0-9_]{6}[A-Za-z0-9_]*",
message = "Usernames can have latin characters, the underscore and "
+ "digits and are at least 6 characters")
@Size(max = 45)
private String username;
//etc
}
和服务:
@Stateless
public class UserService {
@PersistenceContext
private EntityManager em;
public boolean isUsernameUnique(String username) {
Query query = em
.createNativeQuery("SELECT r1_check_unique_username(?)");
short i = 0;
query.setParameter(++i, username);
return (boolean) query.getSingleResult();
}
}
如果我输入超过45个字符的用户名,MySql会抛出异常 - cause()
的输出为:
INFO: --->
EXCEPTION:::::MSG
=================
EJBException:::::null
PersistenceException:::::Exception[EclipseLink-4002](Eclipse Persistence Services\
- 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data\
too long for column 'username_given' at row 198
Error Code: 1406
Call: SELECT r1_check_unique_username(?)
bind => [1 parameter bound]
Query: DataReadQuery(sql="SELECT r1_check_unique_username(?)")
DatabaseException:::::
Internal Exception: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data\
too long for column 'username_given' at row 198
Error Code: 1406
Call: SELECT r1_check_unique_username(?)
bind => [1 parameter bound]
Query: DataReadQuery(sql="SELECT r1_check_unique_username(?)")
MysqlDataTruncation:::::Data truncation:Data too long for column 'username_given'\
at row 198
FIN
我处理它的方式显示"Max 45 chars"
消息。
但是这种处理(明确检查错误代码)有点臭。另一方面,如果我没有抛出ValidatorException(并且只是catch (Exception e) {}
也是臭的)bean验证开始(由@Size(max = 45)
引起的那个)但我仍然看到异常玻璃鱼日志中的痕迹。
问题
catch (Exception ignore) {}
还是检查我手动获取的异常 - 理想情况下我可以要求验证器在之后运行 > User
中的(所有)bean验证 - 并且只有在这些验证通过时才会生效)