Hibernate删除查询

时间:2012-11-03 15:35:38

标签: java hibernate

当我尝试使用

从数据库中删除条目时
session.delete(object) 

然后我可以做到以下几点:

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

2)如果数据库中没有该行,则只执行选择查询

但同样,这不是更新的情况。无论是否存在DB行,只会执行更新查询。

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

修改:

我正在使用hibernate 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 个答案:

答案 0 :(得分:65)

原因是为了删除对象,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();

答案 1 :(得分:22)

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

Hibernate对象状态

  

瞬态 - 对象处于暂态状态(如果已经存在)   实例化,仍然没有与Hibernate会话相关联。

     

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

     

Detached - 分离的实例是一个持久的对象,   但其会议已经结束。

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()时,以及每当您使用load()get()list(),{iterate()检索对象时{1}}或scroll(),该对象被添加到Session的内部缓存中。这是跟踪各种对象的变化的地方。

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

从会话到应用程序的Interceptor接口和侦听器回调允许应用程序在保存,更新,删除或加载之前检查和/或操纵持久对象的属性。 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状态。

来到你​​的场景 -

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

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

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

答案 2 :(得分:3)

我不确定但是:

  • 如果使用非瞬态对象调用delete方法,则表示首先从DB中获取对象。所以看到一个select语句是正常的。也许最后你会看到2个选择+ 1删除?

  • 如果使用临时对象调用delete方法,则可能有cascade="delete"或类似的东西需要首先检索对象,以便可以执行“嵌套操作”这是必需的。


修改 使用瞬态实例调用delete()意味着执行类似的操作:

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

这将删除id为1234的行,即使该对象是Hibernate未检索到的简单pojo,也不存在于其会话缓存中,而不是由Hibernate管理。

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

答案 3 :(得分: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);

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