40

当我尝试从数据库中删除条目时,使用

session.delete(object) 

那么我可以执行以下操作:

1) 如果该行存在于 DB 中,则执行两个 SQL 查询:一个选择,然后一个删除

2)如果数据库中不存在该行,则仅执行选择查询

但这又不是更新的情况。无论数据库行是否存在,都只会执行更新查询。

请让我知道为什么删除操作会出现这种行为。这不是一个性能问题,因为有两个查询而不是一个查询?

编辑:

我正在使用休眠 3.2.5

示例代码:

SessionFactory sessionFactory = new Configuration().configure("student.cfg.xml").buildSessionFactory();
    Session session = sessionFactory.openSession();
    Student student = new Student();
    student.setFirstName("AAA");
    student.setLastName("BBB");
    student.setCity("CCC");
    student.setState("DDD");
    student.setCountry("EEE");
    student.setId("FFF");
    session.delete(student);
    session.flush();
            session.close();

cfg.xml

<property name="hibernate.connection.username">system</property>
    <property name="hibernate.connection.password">XXX</property>
    <property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
    <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521/orcl</property>      
    <property name="hibernate.jdbc.batch_size">30</property>
    <property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
    <property name="hibernate.cache.use_query_cache">false</property>
    <property name="hibernate.cache.use_second_level_cache">false</property>
    <property name="hibernate.connection.release_mode">after_transaction</property>
    <property name="hibernate.connection.autocommit">true</property>
    <property name="hibernate.connection.pool_size">0</property>
    <property name="hibernate.current_session_context_class">thread</property>    
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.hbm2ddl.auto">update</property>        

hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.infy.model.Student" table="STUDENT">
    <id name="id" column="ID">
        <generator class="assigned"></generator>
    </id>
    <property name="firstName" type="string" column="FIRSTNAME"></property>
    <property name="lastName" type="string" column="LASTNAME"></property>
    <property name="city" type="string" column="CITY"></property>
    <property name="state" type="string" column="STATE"></property>
    <property name="country" type="string" column="COUNTRY"></property>        
</class>

4

4 回答 4

83

原因是为了删除一个对象,Hibernate 要求该对象处于持久状态。因此,Hibernate 首先获取对象(SELECT),然后将其删除(DELETE)。

为什么 Hibernate 需要先获取对象?原因是可能启用了 Hibernate 拦截器(http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/events.html),并且对象必须通过这些拦截器才能完成其生命周期. 如果直接在数据库中删除行,则拦截器将不会运行。

另一方面,可以使用批量操作在单个 SQL DELETE 语句中删除实体:

Query q = session.createQuery("delete Entity where id = X");
q.executeUpdate();
于 2012-11-05T21:50:34.577 回答
24

要理解hibernate的这种特殊行为,理解一些hibernate概念很重要——

休眠对象状态

Transient - 如果一个对象已经被实例化并且仍然没有与 Hibernate 会话相关联,则它处于瞬态状态。

持久性 - 持久性实例在数据库中具有表示形式和标识符值。它可能刚刚被保存或加载,但是,根据定义,它在 Session 的范围内。

Detached - 一个分离的实例是一个已经被持久化的对象,但是它的 Session 已经被关闭了。

http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/objectstate.html#objectstate-overview

事务后写

接下来要了解的是“事务写入落后”。当附加到休眠会话的对象被修改时,它们不会立即传播到数据库。Hibernate 这样做至少有两个不同的原因。

  • 执行批量插入和更新。
  • 仅传播最后一次更改。如果一个对象被多次更新,它仍然只触发一个更新语句。

http://learningviacode.blogspot.com/2012/02/write-behind-technique-in-hibernate.html

一级缓存

Hibernate 有一个叫做“一级缓存”的东西。每当您将对象传递给save()update()或时,以及每当您使用、、或saveOrUpdate()检索对象时,该对象都会添加到 Session 的内部缓存中。这是它跟踪各种对象的变化的地方。load()get()list()iterate()scroll()

Hibernate 拦截器和对象生命周期监听器 -

从会话到应用程序的拦截器接口和侦听器回调,允许应用程序在保存、更新、删除或加载持久对象之前检查和/或操作持久对象的属性。 http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html#d0e3069


本节已更新

级联

Hibernate 允许应用程序定义关联之间的级联关系。例如,'cascade-delete'从父节点到子节点的关联将导致在删除父节点时删除所有子节点。

那么,为什么这些很重要。

为了能够进行事务后写,能够跟踪对对象(对象图)的多个更改并能够执行生命周期回调,hibernate 需要知道对象是否存在transient/detached并且它需要将对象放在它的第一级在对底层对象和关联关系进行任何更改之前进行缓存。

这就是为什么hibernate (有时)会发出一条'SELECT'语句,在对对象进行更改之前将对象(如果尚未加载)加载到它的一级缓存中。

为什么 hibernate 有时只发出 'SELECT' 语句?

Hibernate 发出一个'SELECT'语句来确定对象处于什么状态。如果 select 语句返回一个对象,则该对象处于detached状态,如果它没有返回一个对象,则该对象处于transient状态。

来到你的场景-

Delete - 'Delete' 发出 SELECT 语句,因为 hibernate 需要知道对象是否存在于数据库中。如果对象存在于数据库中,hibernate 将其视为detached,然后将其重新附加到会话并处理删除生命周期。

更新- 由于您是显式调用'Update'而不是'SaveOrUpdate',因此休眠会盲目地假定对象处于detached状态,将给定对象重新附加到会话一级缓存并处理更新生命周期。如果与hibernate的假设相反,该对象在数据库中不存在,则在会话刷新时抛出异常。

SaveOrUpdate - 如果调用'SaveOrUpdate',hibernate 必须确定对象的状态,因此它使用 SELECT 语句来确定对象是否处于Transient/Detached状态。如果对象处于transient状态,则处理'insert'生命周期;如果对象处于detached状态,则处理'Update'生命周期。

于 2012-11-06T13:31:56.920 回答
3

我不确定,但是:

  • 如果您使用非瞬态对象调用 delete 方法,这意味着首先从数据库中获取对象。所以看到select语句是正常的。也许最后你看到2选择+1删除?

  • 如果您使用瞬态对象调用 delete 方法,那么您可能有一个cascade="delete"或类似的东西需要首先检索对象,以便在需要时可以执行“嵌套操作”。


编辑: 使用瞬态实例调用 delete() 意味着执行以下操作:

MyEntity entity = new MyEntity();
entity.setId(1234);
session.delete(entity);

这将删除 id 为 1234 的行,即使该对象是一个简单的 pojo,它没有被 Hibernate 检索到,不存在于其会话缓存中,也完全不受 Hibernate 管理。

如果你有一个实体关联,Hibernate 可能必须获取完整的实体,以便它知道删除是否应该级联到关联的实体。

于 2012-11-04T13:15:24.473 回答
1

而不是使用

session.delete(对象)

采用

getHibernateTemplate().delete(object)

既可select 查询又可delete使用getHibernateTemplate()

select查询中,您必须使用DetachedCriteriaCriteria

选择查询示例

List<foo> fooList = new ArrayList<foo>();
DetachedCriteria queryCriteria = DetachedCriteria.forClass(foo.class);
queryCriteria.add(Restrictions.eq("Column_name",restriction));
fooList = getHibernateTemplate().findByCriteria(queryCriteria);

在休眠中避免使用会话,这里我不确定但问题只是因为会话使用而发生

于 2012-11-09T09:28:44.210 回答