为什么实体 bean 字段 (java.util.Date) 在更新后更改值
Why Entity bean field (java.util.Date) changes value after an update
我有一个实体 bean“用户”,它有一个 java.util.Date 字段。当我出于某种奇怪的原因更新 JSF 页面上的任何用户字段(即 phone 号码、公司名称等)时,数据库中日期字段的值发生变化。我的开发环境是 Windows10.
上的 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 的任何字段(即 phone 数字),出生日期值“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”?任何的想法?
这是将 Bob 添加到系统时的 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]]]
这是更新 Bob 时的 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]]]
我有一个显示所有用户并允许更新用户信息的 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;
............
}
这是class与数据库交互
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 {
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: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;
}
}
这是处理日期字段的添加用户 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:commandButton>
</h:panelGroup>
</f:facet>
</h:panelGrid>
这是 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; //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 the pattern symbols are specified in the documentation of java.text.SimpleDateFormat
. This documentation says,您应该使用 M
几个月。
Letter
Date or time component
Presentation
Examples
M
Month in year (context sensitive)
Month
July; Jul; 07
m
Minute in hour
Number
30
因此,相应地调整模式:
<f:convertDateTime type="date" pattern="yyyy-MM-dd" />
与具体问题无关:指定pattern
属性时忽略type
属性。下面的声明同样可以。
<f:convertDateTime pattern="yyyy-MM-dd" />
这在its documentation中也有规定。
If a pattern
has been specified, its syntax must conform the rules specified by java.text.SimpleDateFormat
. Such a pattern will be used to parse, and the type
, dateStyle
, and timeStyle
properties will be ignored.
我有一个实体 bean“用户”,它有一个 java.util.Date 字段。当我出于某种奇怪的原因更新 JSF 页面上的任何用户字段(即 phone 号码、公司名称等)时,数据库中日期字段的值发生变化。我的开发环境是 Windows10.
上的 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 的任何字段(即 phone 数字),出生日期值“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”?任何的想法?
这是将 Bob 添加到系统时的 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]]]
这是更新 Bob 时的 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]]]
我有一个显示所有用户并允许更新用户信息的 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;
............
}
这是class与数据库交互
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 {
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: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;
}
}
这是处理日期字段的添加用户 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:commandButton>
</h:panelGroup>
</f:facet>
</h:panelGrid>
这是 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; //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 the pattern symbols are specified in the documentation of java.text.SimpleDateFormat
. This documentation says,您应该使用 M
几个月。
Letter Date or time component Presentation Examples M Month in year (context sensitive) Month July; Jul; 07 m Minute in hour Number 30
因此,相应地调整模式:
<f:convertDateTime type="date" pattern="yyyy-MM-dd" />
与具体问题无关:指定pattern
属性时忽略type
属性。下面的声明同样可以。
<f:convertDateTime pattern="yyyy-MM-dd" />
这在its documentation中也有规定。
If a
pattern
has been specified, its syntax must conform the rules specified byjava.text.SimpleDateFormat
. Such a pattern will be used to parse, and thetype
,dateStyle
, andtimeStyle
properties will be ignored.