You need to enable JavaScript to run this app. 为何更新User实体非日期字段时java.util.Date值异常变化
优惠活动
大模型
产品
解决方案
定价
更多

为何更新User实体非日期字段时java.util.Date值异常变化

JSF+JPA项目日期字段异常变动问题排查

基础开发环境

  • 操作系统:Windows 10
  • 开发IDE:NetBeans 12.0
  • 持久层框架:EclipseLink JPA
  • 数据库:Apache Derby
  • 应用服务器:GlassFish 5.1

核心异常

在JSF页面上更新User实体的手机号、公司名称这类非日期字段时,数据库中存储的出生日期字段值会出现无交互的异常变动。

具体复现流程

  1. 新增出生日期为1980-08-20的用户Bob时,系统会无交互地为该日期值固定追加凌晨3:38的时间戳,无论当日在哪个时段发起新增操作,这个附加的时间值完全固定。
  2. 新增完成后从数据库读取Bob的信息在JSF页面展示,出生日期可以正常显示为预期的1980-08-20。
  3. 发起非日期字段的更新操作(比如修改手机号),通过调试器确认,传入LoginRequestSessionBean更新逻辑的出生日期值,仍然是带3:38时间戳的原始值。
  4. 更新操作完成后再次从数据库读取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.10 20:06:05