Oracle Deadlock при загрузке данных приложения Hibernate только для чтения - PullRequest
5 голосов
/ 19 марта 2009

Возникла ошибка Oracle Deadlock (org.hibernate.util.JDBCExceptionReporter - ORA-00060: обнаружена тупиковая ситуация при ожидании ресурса). Предполагается, что проблема связана с процессом, который выполняет операции только для чтения, используя Hibernate, в то время как другой процесс выполняет обновление в той же строке.

Процесс, доступный только для чтения, настраивается с использованием Hibernate и Spring. Мы явно не определили транзакцию для сервиса. Хотя это может и не быть идеальным - я не понимаю, почему Hibernate пытается установить монопольную блокировку строки, когда не выполняются операции сохранения / обновления - только получение / загрузка.

Итак, мой вопрос: пытается ли Hibernate, когда не определено явное управление транзакциями, получить блокировку чтения / записи для строки, даже если выполняется только «загрузка» объекта. Сохранение / обновление не выполняется.

Возможно ли, что определение транзакции вокруг службы, которая загружает данные, а затем, в частности, произнесение READONLY для транзакции атрибутов, приведет к тому, что Hibernate проигнорирует уже существующую блокировку строки и просто загрузит данные только для чтения?

Вот несколько примеров кода:

Для загрузки записи мы используем HibernateDaoTemplate

public class HibernatePurchaseOrderDataService extends HibernateDaoSupport implements PurchaseOrderDataService {
    public PurchaseOrderData retrieveById(Long id) {
        return (PurchaseOrderData)getHibernateTemplate().get(PurchaseOrderData.class, id);
    }
}

Конфигурация Spring для службы, вызывающей этот метод:

<bean id="orderDataService"
      class="com.example.orderdata.HibernatePurchaseOrderDataService">
    <property name="sessionFactory" ref="orderDataSessionFactory"/>
</bean>

<bean id="orderDataSessionFactory"
      class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="hibernateDataSource"/>
    <property name="hibernateProperties" ref="hibernateProperties"/>
    <property name="mappingResources">
        <list>
            <value>com/example/orderdata/PurchaseOrderData.hbm.xml</value>
            <value>com/example/orderdata/PurchaseOrderItem.hbm.xml</value>
        </list>
    </property>
</bean>

Фактическая тупиковая ситуация возникает в одной из записей PurchaseOrderItem, загружаемых при вызове загрузки PurchaseOrder.

Приведет ли это к взаимоблокировке, если загружаемая запись была заблокирована другим процессом? И если это так - решит ли проблема добавление оболочки транзакций, такой как приведенная ниже?

<bean id="txWrappedOrderDataService"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="transactionManager"/>
    <property name="target" ref="orderDataService"/>
    <property name="transactionAttributes">
    <props>
        <!-- all methods require a transaction -->
        <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
    </props>
    </property>
</bean>

Обновление: команда DataBase увидела на сервере сообщения трассировки, которые, кажется, указывают на то, что наш процесс «только для чтения» фактически выполняет запись в базу данных автоматически. В журнале есть команды «ОБНОВИТЬ», которые выполняются для тех столбцов, которые мы читаем из базы данных. Похоже, что Hibernate автоматически записывает эти записи обратно в базу данных (хотя мы этого не просим). Это, вероятно, объясняет, почему существует тупик.

Может ли это быть из-за Сеансовой FLUSH или чего-то подобного? Больше похоже на то, что решение может заключаться в использовании оболочки транзакций с readOnly на нем ...

Ответы [ 6 ]

2 голосов
/ 31 марта 2009

Принудительные обновления могут происходить в спящем режиме, когда вы используете сеттеры, которые манипулируют значением, которое они фактически устанавливают. Примером может служить установщик атрибута String, который заменяет значение null на «». Вероятным кандидатом также являются коллекции. Убедитесь, что сеттеры не заменяют содержащуюся коллекцию. Если вы замените коллекцию объекта другой коллекцией, содержащей такое же содержимое, hibernate не сможет это понять и обновить полную коллекцию.

1 голос
/ 23 апреля 2009

Мы наконец определили, что решение заключалось в том, чтобы заключить его в транзакцию только для чтения.

Мне непонятно, почему мы вообще не использовали сеттеры (просто читали данные) - в базе данных ничего не менялось. Но по какой-то причине Hibernate пытался перезаписать те же данные обратно и вызывал блокировку, когда другой процесс пытался прочитать эти записи.

При использовании транзакции readOnly проблема исчезла!

<bean id="txWrappedOrderDataService"
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target" ref="orderDataService"/>
<property name="transactionAttributes">
    <props>
        <!-- all methods require a transaction -->
        <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
    </props>
</property>

0 голосов
/ 24 июля 2010

Я видел эту проблему в нашей системе, когда у нас отсутствовали индексы. Запросы, которые выполняются в базе данных, выполняются слишком долго из-за отсутствия индексов в ключевых столбцах, что блокирует таблицу.

0 голосов
/ 24 июля 2010

Что интересно сделать, это добавить

log4j.logger.org.hibernate.persister.entity.AbstractEntityPersister = TRACE

в вашей конфигурации log4j. При этом hibernate будет регистрировать, почему объект нуждается в обновлении в базе данных. У нас были некоторые проблемы с сущностями, возвращающими «», когда свойство было пустым, вызывая обновления в БД. Благодаря этому мы смогли точно определить такие проблемы.

0 голосов
/ 14 апреля 2009

Дженс прав

Чтобы добавить - вам нужно внимательно осмотреть ваши сеттеры и геттеры и посмотреть, возвращают ли они разные значения при разных вызовах. например, new Date () - это будет возвращать новое значение - каждый раз, когда оно вызывается и заставит hibernate думать, что объект изменился

0 голосов
/ 20 марта 2009

Проверены ли триггеры в базе данных? Вы уверены, что это Hibernate, а не какой-то другой процесс, обновляющий те же строки? Может быть, есть столбец, в котором хранится метка времени последнего чтения, и она обновляется каждый раз, когда читается строка (хотя я не могу вспомнить, что вы можете сделать триггеры SELECT) ...

...