Подтвердить что ты не робот

Запрос на удаление Hibernate

Когда я пытаюсь удалить запись из db, используя

session.delete(object) 

то я могу сделать следующее:

1) Если строка присутствует в БД, выполняется два 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>

4b9b3361

Ответ 1

Причина в том, что для удаления объекта 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();

Ответ 2

Чтобы понять это своеобразное поведение спящего режима, важно понять несколько концепций спящего режима -

Спящие состояния объекта

Transient - объект находится в состоянии переходного процесса, если он был экземпляр и все еще не связан с сеансом Hibernate.

Постоянный - постоянный экземпляр имеет представление в базы данных и значения идентификатора. Возможно, он был сохранен или однако он по определению входит в объем сеанса.

Отдельно - отдельный экземпляр - это объект, который был постоянным, но его сессия закрыта.

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() или scroll(), этот объект добавляется к внутренний кеш сеанса. Здесь отслеживаются изменения для различных объектов.

Перехватчики 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, а затем повторно завершает его в сеанс и обрабатывает жизненный цикл delete.

Обновить. Поскольку вы явно вызываете 'Update' вместо 'SaveOrUpdate', hibernate слепо предполагает, что объект находится в состоянии detached, повторно прикрепляет данный объект к первому уровню сеанса кэшировать и обрабатывать жизненный цикл обновления. Если выяснится, что объект не существует в базе данных вопреки предположению о спящем режиме, исключение генерируется при сбросе сеанса.

SaveOrUpdate. Если вы вызываете 'SaveOrUpdate', hibernate должен определить состояние объекта, поэтому он использует инструкцию SELECT, чтобы определить, находится ли объект в состоянии transient/detached. Если объект находится в состоянии transient, он обрабатывает жизненный цикл 'insert', и если объект находится в состоянии detached, он обрабатывает жизненный цикл 'Update'.

Ответ 3

Я не уверен, но:

  • Если вы вызываете метод delete с непереходным объектом, это означает, что сначала вытащил объект из БД. Так что нормально видеть оператор select. Возможно, в конце вы видите 2 select + 1 delete?

  • Если вы вызываете метод удаления с временным объектом, то возможно, что у вас есть cascade="delete" или что-то подобное, которое требует, чтобы сначала получить объект, чтобы можно было выполнить "вложенные действия", если оно требуется.


Edit: Вызов delete() с временным экземпляром означает делать что-то вроде этого:

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

Это приведет к удалению строки с идентификатором 1234, даже если объект является простым pojo, не полученным Hibernate, не присутствующим в кеше сеанса, вообще не управляемым Hibernate.

Если у вас есть ассоциация сущностей, Hibernate, вероятно, должен получить полный объект, чтобы он знал, следует ли каскадировать удаление для связанных объектов.

Ответ 4

вместо

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

В hibernate избегайте использования сеанса, здесь я не уверен, но проблема возникает только из-за использования сеанса