当我试图从数据库中删除一个条目时,使用
session.delete(object)
然后我可以:
1)如果数据库中存在该行,则会执行两个SQL查询:选择然后删除
2)如果数据库中不存在该行,则只执行选择查询
但同样,这不是更新的情况。不管数据库行是否存在,都只执行更新查询。
请让我知道为什么这种行为的删除操作。这难道不是一个性能问题,因为两个查询被命中而不是一个?
编辑:
我用的是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>
5条答案
按热度按时间bgtovc5b1#
原因是,要删除一个对象,Hibernate要求该对象处于持久状态。因此,Hibernate首先获取对象(SELECT),然后删除它(SELECT)。
为什么Hibernate需要先获取对象?原因是可能启用了Hibernate拦截器(https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/events.html),对象必须通过这些拦截器才能完成其生命周期。如果直接在数据库中删除行,拦截器将不会运行。
另一方面,可以使用批量操作在一个SQL语句中删除实体:
ffscu2ro2#
要理解hibernate的这种特殊行为,重要的是要理解一些hibernate的概念-
休眠对象状态
Transient -如果一个对象已经示例化,但仍然没有与Hibernate会话关联,则该对象处于Transient状态。
持久化-持久化示例在数据库中具有表示形式和标识符值。它可能只是被保存或加载,但是,根据定义,它在Session的范围内。
Detached -一个分离的示例是一个持久化的对象,但是它的Session已经关闭。
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/objectstate.html#objectstate-overview
事务后写
接下来要理解的是“Transaction Write behind”。当附加到休眠会话的对象被修改时,它们不会立即传播到数据库。Hibernate这样做至少有两个不同的原因。
http://learningviacode.blogspot.com/2012/02/write-behind-technique-in-hibernate.html
一级缓存
Hibernate有一个叫做“一级缓存”的东西。无论何时将对象传递给
save()
、update()
或saveOrUpdate()
,无论何时使用load()
、get()
、list()
、iterate()
或scroll()
检索对象,该对象都会添加到Session的内部缓存中。这是它跟踪各种对象的更改的地方。休眠拦截器和对象监听器-
Interceptor接口和从会话到应用程序的侦听器回调允许应用程序在保存、更新、删除或加载持久对象之前检查和/或操作持久对象的属性。http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html#d0e3069
本节已更新
级联
Hibernate允许应用程序定义关联之间的级联关系。例如,从父到子关联的
'cascade-delete'
将导致在删除父时删除所有子。为什么这些很重要
为了能够进行transaction write-behind,为了能够跟踪对象(对象图)的多个更改,并且能够执行生命周期回调,Hibernate需要知道对象是否是
transient/detached
,并且在对底层对象和相关关系进行任何更改之前,它需要将对象放在其第一级缓存中。这就是为什么hibernate**(有时)**发出一个
'SELECT'
语句来加载对象(如果它还没有加载)到它的第一级缓存中,然后再对它进行修改。为什么Hibernate只在某些时候发出SELECT语句?
Hibernate发出一条
'SELECT'
语句来确定对象的状态。如果select语句返回一个对象,则该对象处于detached
状态,如果它没有返回对象,则该对象处于transient
状态。来到你的场景-
Delete-'Delete'发出了一个SELECT语句,因为Hibernate需要知道对象是否存在于数据库中。如果对象存在于数据库中,hibernate将其视为
detached
,然后将其重新连接到会话并处理删除生命周期。Update-由于您显式调用的是
'Update'
而不是'SaveOrUpdate'
,因此hibernate会盲目地假设对象处于detached
状态,将给定对象重新附加到会话一级缓存,并处理更新生命周期。如果与hibernate的假设相反,数据库中不存在该对象,则在会话刷新时抛出异常。SaveOrUpdate-如果调用
'SaveOrUpdate'
,Hibernate需要判断对象的状态,所以使用SELECT语句判断对象是否处于Transient/Detached
状态。如果对象处于transient
状态,则处理'insert'
生命周期,如果对象处于detached
状态,则处理'Update'
生命周期。w8f9ii693#
我不确定,但是:
cascade="delete"
或类似的东西,它需要首先检索对象,以便在需要时执行“嵌套操作”。**Edit:**使用transmant示例调用delete()意味着做类似的事情:
这将删除id为1234的行,即使该对象是一个简单的pojo,没有被Hibernate检索,没有出现在其会话缓存中,根本没有被Hibernate管理。
如果你有一个实体关联,Hibernate可能需要获取完整的实体,这样它就知道删除是否应该级联到关联的实体。
lp0sw83n4#
而不是使用
session.delete(object)
使用
在
select
查询和delete
查询的两个地方都使用getHibernateTemplate()
在
select
查询中,必须使用DetachedCriteria
或Criteria
选择查询示例
在休眠避免使用会话,在这里我不确定,但问题发生只是因为会话的使用
uxh89sit5#
可以使用
EntityManager
类。它是与持久化上下文交互和管理实体示例的中心接口。它提供了CRUD(创建,读取,更新,删除)操作的方法。entityManager.getReference
方法用于获取对具有指定id的Object实体的引用。getReference方法通常用于获取实体的轻量级代理,而无需立即从数据库获取其数据。这对于删除操作很有用,因为它避免了不必要的数据检索。通过使用
entityManager.remove
,您可以指示JPA
提供程序(如Hibernate)在下一次刷新或提交操作期间计划删除实体。然后,JPA
提供程序将生成适当的SQL DELETE
语句,以便从数据库中删除该实体。