【问题标题】:Hibernate: Update an object -> also unwanted delete statements are performedHibernate:更新一个对象 -> 还执行了不需要的删除语句
【发布时间】:2010-12-15 17:09:40
【问题描述】:

我在我的 Java 应用程序中使用 Hibernate 作为 ORM。 我有一个 Project Person manytomany-relation 并且 Project 是映射所有者。

现在我有一个问题,我想更新项目。该项目有一个 id、一个名称、...和一组人员。做一次更新,Project_Person联接表中的人都被删除了,控制台上的SQL语句:

Hibernate: update Project set description=?, name=? where id=?
Hibernate: delete from Project_Person where project_id=?

但我不希望执行删除语句。

我的 Java 应用程序中有这个:

this.projService.updateProject(p);

其中 p 是项目,但以 POJO 方式没有人员集。所以我想,为了让它“准备好休眠”,我在一个单独的事务中创建了一个 findProjectById,所以我从数据库中获取项目:

Project proj = this.projService.findProjectById(p.getId());
proj.setName(p.getName());
proj.setDescription(p.getDescription());

所以我得到了 Hibernate-ready Project 对象,更改了值,然后告诉他更新项目。但是 Set 在调试器视图中是 PersistentSet 并且其中没有 Persons (我认为,因为它们是延迟加载的)。 但是更新了

session.update(p);

在一个新事务中,我得到了更新语句和不需要的删除语句。 如何避免删除语句?

我是否必须创建一个特殊的 SQL 语句,以便只更新我要更新的数据库表字段?还是有其他/更好的解决方案?

最好的问候。


更新 这会引发 LazyInitializationException:

public Project findProjectById(int projectId) {
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session sess = sessionFactory.getCurrentSession();
    Transaction tx = sess.beginTransaction();
    try {
        Project project = (Project)sess.createQuery("from Project where id = "+projectId).list().get(0);
        tx.commit();
        System.out.println(project.getPersons().size());
        return project;
    } catch (IndexOutOfBoundsException ex) {
        return null;
    }
}

调试器的屏幕截图

http://img94.imageshack.us/img94/2020/screendebugger.png

HibernateUtil

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    // SessionFactory of Hibernate
    private static final SessionFactory sessionFactory;
    static {
        try {
            sessionFactory = new Configuration().configure("/hibernate.cfg.xml").buildSessionFactory();
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed. " + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

tx.commit() 之前的屏幕截图

http://img808.imageshack.us/img808/9624/screendebugger2.png

【问题讨论】:

  • 很奇怪。在提交事务之前尝试打印大小。
  • 我将调试器的屏幕截图放在 tx.commit() 行之前。更新:将 getPersons().size() 放在 tx.commit() 行之前,我明白了!
  • 所以,提交后会话关闭。那么我该怎么做才能用不需要的 SQL 删除语句来解决这个问题呢?

标签: java hibernate


【解决方案1】:

这意味着(如您所建议的)您的收藏是空的。但这不是由于延迟加载 - 调试器将允许您加载集合。也许它是空的还有另一个原因。通过显示collection.size() 确认这一点。

问题的另一部分是级联。如果您已定义级联 delete-orphan,则将其删除,或确保集合已实际填充。

【讨论】:

  • 我没有定义像 delete-orphan 这样的级联。我更深入地研究了项目的 Setpersons:没有 Person。集合的 size() 将始终返回 LazyInitialization 错误。
  • 我举了一个例子,它在我的问题中抛出了一个 LazyInit 异常。
  • 然后将断点放置在会话仍未关闭的方法中。
  • 我不太确定,如果我理解正确的话,但是我在我的问题上放了一个屏幕截图的链接,这样你就可以看到调试器视图和带有断点的源代码。跨度>
  • @Tim - 在返回对象之前将断点放在projectService 中。会话在那里可用(并且在控制器中关闭,也许您的服务层周围有一个事务管理器)
【解决方案2】:

你不需要调用“更新”。

以下代码:

Project proj = this.projService.findProjectById(p.getId());
proj.setName(p.getName());
proj.setDescription(p.getDescription());

然后不需要更新,因为项目引用是持久的,对它的任何更改都将被保存。这是 hibernate 和其他 ORM 的一个特性,称为透明持久性。

有关休眠更新方法的更多信息,请参阅here

【讨论】:

  • 是的,但是我的模型关闭了交易,所以我需要 tzo 在我的情况下调用它。您的链接没问题,但在这种情况下我不使用它。
【解决方案3】:

如果您在新事务中更新 Project 对象,您可能应该使用将对象与新 Session 关联

session.merge(project)

也许检测到未初始化的 PersistentSet,将其替换为来自新 Session 的新 PersistentSet,并确保不删除 Set 的条目。否则 Hibernate 可能无法处理 Set,因为创建它的事务已经关闭并假定它是空的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 2016-03-17
    • 2018-07-08
    • 1970-01-01
    • 2012-09-11
    • 1970-01-01
    相关资源
    最近更新 更多