为何更新User实体非日期字段时java.util.Date值异常变化
JSF+JPA项目日期字段异常变动问题排查
基础开发环境
- 操作系统:Windows 10
- 开发IDE:NetBeans 12.0
- 持久层框架:EclipseLink JPA
- 数据库:Apache Derby
- 应用服务器:GlassFish 5.1
核心异常
在JSF页面上更新User实体的手机号、公司名称这类非日期字段时,数据库中存储的出生日期字段值会出现无交互的异常变动。
具体复现流程
- 新增出生日期为
1980-08-20的用户Bob时,系统会无交互地为该日期值固定追加凌晨3:38的时间戳,无论当日在哪个时段发起新增操作,这个附加的时间值完全固定。 - 新增完成后从数据库读取Bob的信息在JSF页面展示,出生日期可以正常显示为预期的
1980-08-20。 - 发起非日期字段的更新操作(比如修改手机号),通过调试器确认,传入
LoginRequestSessionBean更新逻辑的出生日期值,仍然是带3:38时间戳的原始值。 - 更新操作完成后再次从数据库读取Bob的信息展示,Bob的出生日期时间戳已经变为凌晨00:00,页面上原本正确显示的
1980-08-20直接错变为1980-01-19。
目前已完成现象复现、日志采集、核心代码梳理,暂未定位到问题根因,寻求异常产生的具体原因。
相关运行日志
新增用户时的SQL执行日志
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]
更新用户时的SQL执行日志
UPDATE PERSISTENCE_USER SET USERROLE = ?, CELLPHONE = ?, DATEOFBIRTH = ?, LASTNAME = ?, USERID = ?, FIRSTNAME = ?, OFFICEPHONE = ?, COMPANY = ? WHERE (ID = ?) -- 参数绑定结果 bind => [consumer, (910)-509-3924, 1980-01-20, Nittelo, Bobnitello, Bob, (818)-456-9011, IBM, 5]
项目核心源码片段
User实体定义
@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; // 省略其余属性、getter/setter方法 }
数据库交互Session Bean
@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, logger,"Error occured while removing a user"); } } public void updateUser(User u) throws ListServiceException{ try { int 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, logger, "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,logger,"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: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; @EJB private LoginRequestSessionBean request; private final ResourceBundle bundle; private List<User> users; public LoginManagment() { 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() { try { users.stream() .filter(e -> e.getCanUpdate() == true) .forEach(n -> request.updateUser(n)); 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; } }
新增用户页面JSF代码
<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:panelGroup> </f:facet>
新增页面对应的JSF受管Bean
@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; 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 errMsg = new FacesMessage(e.getMessage()); FacesContext.getCurrentInstance().addMessage(null, errMsg); } } }
内容的提问来源于stack exchange,提问作者Darvin
相关产品推荐
相关产品推荐

