当我尝试使用
从数据库中删除条目时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>
答案 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
查询中,您必须使用DetachedCriteria
或Criteria
选择查询的示例
List<foo> fooList = new ArrayList<foo>();
DetachedCriteria queryCriteria = DetachedCriteria.forClass(foo.class);
queryCriteria.add(Restrictions.eq("Column_name",restriction));
fooList = getHibernateTemplate().findByCriteria(queryCriteria);
在休眠中避免使用会话,这里我不确定但是因为会话使用而出现问题