问题描述
我有这个实体 bean“用户”,它有一个 java.util.Date 字段。当我出于某种奇怪的原因更新 JSF 页面上的任何用户字段(即电话号码、公司名称等)时,数据库中日期字段的值会发生变化。我的开发环境是 Windows 10 上的 Netbeans 12.0、EclipseLink JPA、Apache Derby DB 和 GlassFish 5.1。
例如,当我添加出生日期为“1980-08-20”的用户 (Bob) 时,JSF 会为 Bob 的用户实体设置值“1/20/80 3:38 AM”,此外,无论如何一天中的时间,我向用户添加“3:38 AM”是始终附加到日期的固定时间。后来,当我从数据库中检索 Bob 并在 JSF 页面上显示他的信息时,他的出生日期正如预期的那样是“1980-08-20”。如果我更新 Bob 的任何字段(即电话号码),“1/20/80 3:38 AM”的出生日期值发送到处理更新的会话 Bean LoginRequestSessionBean(我通过调试器验证了它)。在更新成功完成并且我从数据库中检索 Bob 并显示在 JSF 页面上之后,Bob 的出生日期是“1/20/80 12:00 AM”而不是“1/20/80 3:38 AM” ”,因此我看到的是“1980-30-19”而不是“1980-08-20”?任何的想法?
INSERT INTO PERSISTENCE_USER (ID,CELLPHONE,COMPANY,DATEOFBIRTH,FirsTNAME,LASTNAME,OFFICEPHONE,PASSWORD,USERID,USERROLE) VALUES (?,?,?)
bind => [5,(910)-509-3924,IBM,1980-01-20,Bob,Nittelo,(818)-456-9012,password,Bobnitello,consumer]]]
这是更新 Bob 时的 sql 日志
UPDATE PERSISTENCE_USER SET USERROLE = ?,CELLPHONE = ?,DATEOFBIRTH = ?,LASTNAME = ?,USERID = ?,FirsTNAME = ?,OFFICEPHONE = ?,COMPANY = ? WHERE (ID = ?)
bind => [consumer,(818)-456-9011,5]]]
我有一个 JSF 页面,显示所有用户并允许更新用户信息。还有另一个 JSF 页面允许向系统添加用户。这是源代码:
@NamedQuery(
name = "updateUser",query = "UPDATE User u SET u.userId=?1,u.userRole=?2,u.cellPhone=?3,u.company=?4,"
+ "u.dateOfBirth=?5,u.firstName=?6,u.lastName=?7,u.officePhone=?8 "
+ "WHERE u.id = ?9"
)
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
private String userId;
@NotNull
private String password;
@NotNull
private String userRole;
@NotNull
private String firstName;
@NotNull
private String lastName;
@Temporal(TemporalType.DATE)
private Date dateOfBirth;
@NotNull
private String officePhone;
private String cellPhone;
@NotNull
private String company;
............
}
这是与数据库交互的类
Stateless
public class LoginRequestSessionBean {
public void addUser(User u) throws ListServiceException {
try {
em.persist(u);
} catch (Exception e) {
throw (ExceptionHandler.wrapException(
e,logger,"Error occured when adding a user "+u.getUserId()));
}
}
public void removeUser(User u) throws ListServiceException{
try{
if(!em.contains(u)){
u = em.merge(u);
}
em.remove(u);
} catch (Exception e) {
throw (ExceptionHandler.wrapException(e,"Error occured while removing a user");
}
}
public void updateUser(User u) throws ListServiceException{
try {
updatedRow = em.createNamedQuery("updateUser")
.setParameter(1,u.getUserId())
.setParameter(2,u.getUserRole())
.setParameter(3,u.getCellPhone())
.setParameter(4,u.getCompany())
.setParameter(5,u.getDateOfBirth())
.setParameter(6,u.getFirstName())
.setParameter(7,u.getLastName())
.setParameter(8,u.getofficePhone())
.setParameter(9,u.getId())
.executeUpdate();
} catch (Exception e) {
throw ExceptionHandler.wrapException(e,"Error occured while updating user");
}
}
public User getUser(Long id) throws ListServiceException{
User user;
try {
user = em.find(User.class,id);
} catch (Exception e) {
throw ExceptionHandler.wrapException(e,"Error occured in"+className+".getuser()");
}
return user;
}
}
这是更新 JSF 页面处理日期列的相关部分
<h:column>
<f:facet name="header">#{bundle.loginmanagementdob}</f:facet>
<h:inputText
p:type="date"
value = "#{l.dateOfBirth}"
size ="15" rendered = "#{l.canUpdate}" >
<f:convertDateTime type="date"
pattern = "yyyy-mm-dd" />
</h:inputText>
<h:outputText value = "#{l.dateOfBirth}"
rendered = "#{not l.canUpdate}" >
<f:convertDateTime
type="date"
pattern = "yyyy-mm-dd" />
</h:outputText>
</h:column>
<h:column>
<f:facet name = "header">Update</f:facet>
<h:commandLink value = "Update"
disabled="#{login.currentUser.userRole == 'delete' or login.currentUser.userRole == 'consumer'}"
action = "#{loginManagment.updateLinkAction(l)}"
rendered = "#{not l.canUpdate}">
</h:commandLink>
</h:column>
<f:facet name="footer">
<h:panelGroup style="display: block; border-color: aquamarine;text-align: center;">
<h:commandButton id="update"
tabindex="1"
value="Save updates"
action="#{loginManagment.saveUpdate}" />
</h:panelGroup>
</f:facet>
这是 JSF 页面的托管 bean:
@Named
@SessionScoped
public class LoginManagment implements Serializable {
private static final long serialVersionUID = 1009L;
private LoginRequestSessionBean request;
private final ResourceBundle bundle; //application resource bundle
public LoginManagment() {
//Get the application's resource bundle
bundle = ResourceBundle.getBundle("webmessages");
}
public List<User> getUsers() {
if ((users == null) || refresh) {
try {
users = request.getUsers();
} catch (EJBException e) {
FacesMessage errMsg = new FacesMessage(e.getMessage());
FacesContext.getCurrentInstance().addMessage(null,errMsg);
}
refresh = false;
}
return users;
}
public void resetUpdateLink() {
users
.stream()
.filter(e -> e.getCanUpdate() == true)
.forEach(e -> e.setCanUpdate(false));
}
public String updateLinkAction(User u) {
u.setCanUpdate(true);
return null;
}
public String saveUpdate() {
Function<User,User> update = n -> {
request.updateUser(n);
return n;
};
try {
users
.stream()
.filter(e -> e.getCanUpdate() == true)
.forEach(update);
resetUpdateLink();
FacesMessage msg = new FacesMessage(bundle.getString("loginmanagementupdatesuccess"));
FacesContext.getCurrentInstance().addMessage(null,msg);
} catch (ListServiceException e) {
FacesMessage errMsg = new FacesMessage(e.getMessage());
FacesContext.getCurrentInstance().addMessage(null,errMsg);
}
logger.exiting(className,"saveUpdate()");
return null;
}
}
<h:outputLabel id="adddoblabel"
for="adduserdob"
style="color: green; font: caption; font-size: large;
font-family: cursive; border-color: aquamarine"
value="#{bundle.adduserdob}" />
<h:inputText id="adduserdob"
p:type="date"
label="Date Of Birth "
title="Date Of Birth"
style="border-color: aquamarine"
value="#{addUser.dateOfBirth}"
required="true"
requiredMessage="#{bundle.adduserdoberror}"
maxlength="30" >
<f:convertDateTime type="date"
pattern = "yyyy-mm-dd" />
</h:inputText>
<f:facet name="footer">
<h:panelGroup style="display: block; border-color: aquamarine;text-align: center;">
<h:commandButton id="addusercommandbutton"
value="Add"
immediate="false"
style="font-size: large; font-family: cursive"
action="#{addUser.addAction}">
</h:commandButton>
</h:panelGroup>
</f:facet>
</h:panelGrid>
@Named
@SessionScoped
public class AddUser implements Serializable {
private static final long serialVersionUID = 1100L;
private String firstName;
private String lastName;
private Date dateOfBirth;
@EJB
private LoginRequestSessionBean request;
private ResourceBundle bundle; //application resource bundle
public AddUser() {
bundle = ResourceBundle.getBundle("webmessages");
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date date){
dateOfBirth=date;
}
private void cleardatafield() {
this.password = null;
this.userId = null;
this.role = null;
this.cellPhone = null;
this.officePhone = null;
this.company = null;
this.dateOfBirth = null;
this.firstName = null;
this.lastName = null;
}
public void addAction() {
try {
User u = new User(getUserId(),getpassword(),getRole(),getFirstName(),getLastName(),getDateOfBirth(),getofficePhone(),getCellPhone(),getCompany());
request.addUser(u);
cleardatafield();
FacesMessage successMsg = new FacesMessage(bundle.getString("addusersuccess"));
FacesContext.getCurrentInstance().addMessage(null,successMsg);
} catch (EJBException e) {
FacesMessage successMsg = new FacesMessage(e.getMessage());
FacesContext.getCurrentInstance().addMessage(null,successMsg);
}
}
}
解决方法
这是因为您已将日期转换器配置为将连字符之间的部分解释为分钟。
<f:convertDateTime type="date" pattern="yyyy-mm-dd" />
根据 its documentation,模式符号在 java.text.SimpleDateFormat
的文档中指定。此文档says您应该使用 M
数月。
信 | 日期或时间组件 | 介绍 | 示例 |
---|---|---|---|
M | 月份(上下文敏感) | 月 | 七月;七月; 07 |
米 | 一小时一分钟 | 数量 | 30 |
因此,相应地调整模式:
<f:convertDateTime type="date" pattern="yyyy-MM-dd" />
与具体问题无关:指定 type
属性时忽略 pattern
属性。以下声明同样适用。
<f:convertDateTime pattern="yyyy-MM-dd" />
这也在 its documentation 中指定。
如果指定了 pattern
,其语法必须符合 java.text.SimpleDateFormat
指定的规则。这样的模式将用于解析,type
、dateStyle
和 timeStyle
属性将被忽略。