Hibernate删除查询

rqmkfv5c  于 2023-10-23  发布在  其他
关注(0)|答案(5)|浏览(152)

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

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>
bgtovc5b

bgtovc5b1#

原因是,要删除一个对象,Hibernate要求该对象处于持久状态。因此,Hibernate首先获取对象(SELECT),然后删除它(SELECT)。
为什么Hibernate需要先获取对象?原因是可能启用了Hibernate拦截器(https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/events.html),对象必须通过这些拦截器才能完成其生命周期。如果直接在数据库中删除行,拦截器将不会运行。
另一方面,可以使用批量操作在一个SQL语句中删除实体:

Query q = session.createQuery("delete Entity where id = X");
q.executeUpdate();
ffscu2ro

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这样做至少有两个不同的原因。

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

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'生命周期。

w8f9ii69

w8f9ii693#

我不确定,但是:

  • 如果你用一个非 transient 对象调用delete方法,这意味着首先从DB中获取对象。所以看到一个select语句是很正常的。也许最后你会看到2选择+ 1删除?
  • 如果你用一个 transient 对象调用delete方法,那么你可能有一个cascade="delete"或类似的东西,它需要首先检索对象,以便在需要时执行“嵌套操作”。
    **Edit:**使用transmant示例调用delete()意味着做类似的事情:
MyEntity entity = new MyEntity();
entity.setId(1234);
session.delete(entity);

这将删除id为1234的行,即使该对象是一个简单的pojo,没有被Hibernate检索,没有出现在其会话缓存中,根本没有被Hibernate管理。
如果你有一个实体关联,Hibernate可能需要获取完整的实体,这样它就知道删除是否应该级联到关联的实体。

lp0sw83n

lp0sw83n4#

而不是使用
session.delete(object)
使用

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);

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

uxh89sit

uxh89sit5#

可以使用EntityManager类。它是与持久化上下文交互和管理实体示例的中心接口。它提供了CRUD(创建,读取,更新,删除)操作的方法。
entityManager.getReference方法用于获取对具有指定id的Object实体的引用。getReference方法通常用于获取实体的轻量级代理,而无需立即从数据库获取其数据。这对于删除操作很有用,因为它避免了不必要的数据检索。
通过使用entityManager.remove,您可以指示JPA提供程序(如Hibernate)在下一次刷新或提交操作期间计划删除实体。然后,JPA提供程序将生成适当的SQL DELETE语句,以便从数据库中删除该实体。

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

@Repository
public class SampleRepositoryImpl {

    @PersistenceContext
    private EntityManager entityManager;
    
...

    @Override
    public void delete(int id) {
        entityManager.remove(entityManager.getReference(Employee.class,id));
    }
   
}

相关问题