问题描述
我在编写代码时遇到了一个奇怪的错误或功能。情况如下:
我们在JavaEE项目中使用PostgreSQL数据库EclipseLink。
我在项目的这一部分中正在从数据库中获取一个实体,即:
User user = userController.get(userId);
然后转到我们的控制器并通过TypedQuery获取用户:
@Stateless
@LocalBean
public class UserController {
private EntityManager em;
public User get(Integer userId){
User retval = null;
TypedQuery<User> = em.createNamedQuery("User.findByUserId",User.class);
q.setParameter("userId",userId);
retval = q.getSingleResult();
}
public User update(final User modified){...}
}
在我的User类中,我有:
@NamedQuery(name = "User.findByUserId",query = "SELECT u FROM User u WHERE u.id = :userId"),
调用结束了,我从数据库中获取了用户对象及其相应的数据。
在我调用userController.get方法的类中,我继续修改此对象上的数据,然后再次调用我们的控制器以更新数据库上的数据
user.setServiceId(1); //any id (integer) pointing to an existing service,this is a ManyToOne relationship
userController.update(user);
这是有趣的地方。在控制器类内部的更新方法中,我修改了User对象,并使用该对象获取主键userId并再次从数据库中获取数据以获取原始数据:
@Stateless
@LocalBean
public class userController {
private EntityManager em;
public User get(Integer userId){...}
public User update(final User modified){
User retval = null;
if(modified != null){
try {
User original = get(modified.getId()); //Here I fetch the current state of the DB
if(original != null){
Set<Modifications> modifications = apply(original,modified); //Method to apply modifications
retval = em.merge(original); //Merge changes into database
em.flush(); //Force data to be persisted
catch(Exception e){
}
return retval;
}
}
但是,original
对象中的字段不反映数据库的状态,而是包含与modified
对象相同的数据。在这种情况下,数据库上的serviceId
是null
,在modified
中将其设置为ID。 original
的{{1}}设置为与serviceId
对象相同的值,即使它应包含从数据库中获取的数据,在这种情况下为modified
我当前的解决方案是在从数据库中获取用户之后,构造一个新的User对象,并修改该新对象上的数据:
null
现在,当我执行更新方法时,User user = userController.get(userId);
User newUser = new User(user);
newUser.setService(service);
userController.update(newUser);
反映了数据库的状态。
也许它反映了持久性上下文中已经存在的original
对象的状态?
但是为什么会这样呢?由于我确实在更新方法中使用user
语句对数据库进行了新的get
调用。
解决方法
您对所有内容(读取和“合并”)都使用相同的EntityManager,因此在这种情况下为空操作。通过EM读入的所有内容均受到管理,因此,如果再次读回它,您将获得相同的实例。只要不对用户进行序列化,就由读取它的EntityManager对其进行“管理”,因此对该ID进行的任何get调用都可以看到同一实例及其更改。
您没有显示如何获取EntityManager,但是我想这不是容器管理的,因为它们将为这些调用注入一个新的对象,然后在完成时为您关闭它们。您尚未显示有关如何连接更新及其所使用的em上下文的任何事务逻辑,但我建议您为这些调用创建一个新的EntityManager。刷新似乎也没有必要,好像更新包装在事务中一样,应该处理刷新更新语句到数据库而无需额外调用。
,如果在管理“用户”实体时调用user.setServiceId(1);
,则该调用将更新数据库行。
将数据保存到数据库后,需要refresh
,以em.refresh(retval)
的形式获取对象的最新状态
您可以在下面找到添加的代码。
@Stateless
@LocalBean
public class userController {
private EntityManager em;
public User get(Integer userId){...}
public User update(final User modified){
User retval = null;
if(modified != null){
try {
User original = get(modified.getId()); //Here I fetch the current state of the DB
if(original != null){
Set<Modifications> modifications = apply(original,modified); //Method to apply modifications
retval = em.merge(original); //Merge changes into database
em.flush(); //Force data to be persisted
em.refresh(retval); // This will fetch the updated data from database
catch(Exception e){
}
return retval;
}
}