2009-03-19 7 views
5

Wir haben einen Oracle-Deadlock-Fehler (org.hibernate.util.JDBCExceptionReporter - ORA-00060: Deadlock beim Warten auf Ressource festgestellt) festgestellt. Es wurde vorgeschlagen, dass das Problem bei einem Prozess auftritt, der schreibgeschützte Operationen mit Hibernate durchführt, während ein anderer Prozess eine Aktualisierung für dieselbe Zeile ausführt.Oracle Deadlock, wenn die Hibernate-Anwendung Daten für die schreibgeschützte Verwendung lädt

Der fragliche schreibgeschützte Prozess wird mit Hibernate und Spring konfiguriert. Wir haben keine Transaktion für den Service explizit definiert. Auch wenn das nicht ideal ist - ich verstehe nicht, warum Hibernate versuchen würde, eine exklusive Sperre für eine Zeile zu erhalten, wenn keine Speicher-/Aktualisierungsoperationen ausgeführt wurden - nur ein get/load.

Also meine Frage ist: Ist Hibernate, wenn keine explizite Transaktionsverwaltung definiert ist, versuchen Sie, eine Lese-/Schreibsperre für eine Zeile zu erhalten, auch wenn nur ein "Laden" eines Objekts ausgeführt wird. Kein Speichern/Aktualisieren wird durchgeführt.

Ist es möglich, dass das Definieren einer Transaktion um den Dienst, der Daten lädt, und READONLY spezifisch auf den transactionAttributes sagt, Hibernate eine bereits vorhandene Zeilensperre ignoriert und nur die Daten für schreibgeschützte Zwecke lädt?

Hier sind einige Code-Beispiele:

Für das Laden der Platte, die wir eine HibernateDaoTemplate verwenden

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

Die Spring-Konfiguration für den Dienst Aufruf dieser Methode ist:

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

die tatsächliche Deadlock tritt bei einem der PurchaseOrderItem-Datensätze auf, die durch den Aufruf des PurchaseOrder geladen werden.

Würde dies zu einem Deadlock führen, wenn der geladene Datensatz von einem anderen Prozess gesperrt wurde? Und wenn ja - würde das Hinzufügen eines Transaktions-Wrappers wie dem folgenden das Problem lösen?

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

Update: Das Datenbank-Team hat auf dem Server Trace-Nachrichten zu sehen, die darauf hindeuten, dass unser „Nur-Lesen“ Prozess tatsächlich wird automatisch in die Datenbank zu schreiben. Es gibt protokollierte "UPDATE" -Befehle, die für die exakten Spalten ausgeführt werden, die wir aus der Datenbank lesen. Es scheint, dass Hibernate diese Datensätze automatisch wieder in die Datenbank schreibt (obwohl wir das nicht verlangen). Das würde wahrscheinlich erklären, warum es einen Stillstand gibt.

Könnte dies wegen einer Session FLUSH oder etwas ähnliches sein? Sieht eher aus wie die Lösung, einen Transaktions-Wrapper mit readOnly zu verwenden ...

Antwort

1

Wir haben schließlich festgestellt, dass die Lösung war es in eine readOnly-Transaktion zu wickeln.

Ich bin nicht klar warum, wir haben die Setter überhaupt nicht verwendet (einfach die Daten lesen) - nichts wurde in der Datenbank geändert. Aber aus irgendeinem Grund versuchte Hibernate, die gleichen Daten zurückzuschreiben und verursachte eine Sperre, als ein anderer Prozess versuchte, diese Datensätze zu lesen.

Mit der readOnly-Transaktion wurde das Problem behoben!

<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

Haben Sie nach Triggern in der Datenbank gesucht? Sind Sie sicher, dass es Hibernate ist und nicht ein anderer Prozess, der dieselben Zeilen aktualisiert? Vielleicht gibt es eine Spalte, die einen Zeitstempel der letzten Lesung speichert, und sie wird jedes Mal aktualisiert, wenn die Zeile gelesen wird (Obwohl ich mich nicht erinnern kann, dass Sie SELECT-Trigger machen könnten) ...

2

Unfreiwillig Aktualisierungen können im Ruhezustand auftreten, wenn Sie Setter verwenden, die den tatsächlich festgelegten Wert ändern. Ein Beispiel wäre ein Setter für ein String-Attribut, das einen Wert von null durch "" ersetzt. Ein wahrscheinlicher Kandidat sind auch Sammlungen. Stellen Sie sicher, dass die Setter die enthaltene Sammlung nicht ersetzen. Wenn Sie eine Sammlung einer Entität durch eine andere Sammlung ersetzen, die denselben Inhalt enthält, kann Hibernate dies nicht erkennen und die gesamte Sammlung aktualisieren.

+0

Dies ist nicht genau das, was geschah - aber schien ähnlich. Die Werte wurden nicht im Code "gesetzt". Hibernate versuchte tatsächlich, die gleichen Daten aus irgendeinem Grund zurückzuschreiben (die Daten in der Datenbank wurden nie auf andere Werte aktualisiert). – jonathanq

0

Jens ist rechts

Hinzufügen on- Sie genau brauchen, um Ihre Getter und Setter zu prüfen und sehen, ob sie einen anderen Wert auf verschiedenen Anrufe zB new Date() zurückkehren - das eine neue wert- zurückkehren würde jeder Zeit es aufgerufen und wird Winterschlaf denken das Objekt hat sich geändert

0

Eine Sache interessant zu tun ist,

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

in Ihrer log4j Konfiguration hinzuzufügen. Auf diese Weise protokolliert der Ruhezustand, warum eine Entität ein Update in der Datenbank benötigt. Wir hatten einige Probleme mit Entitäten, die "" zurückgaben, wenn eine Eigenschaft null war und Aktualisierungen in der Datenbank verursachte. Dadurch konnten wir solche Probleme feststellen.

0

Ich habe dieses Problem in unserem System auftreten, wenn wir fehlende Indizes hatten. Die Abfragen, die in der Datenbank ausgeführt werden, werden aufgrund fehlender Indizes für Schlüsselspalten zu lange ausgeführt, wodurch die Tabelle gesperrt wird.