-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Hibernate + H2 DB: Öffnen einer zweiten Session => Timeout
PostPosted: Thu Mar 11, 2010 7:45 am 
Beginner
Beginner

Joined: Thu Oct 04, 2007 12:22 pm
Posts: 48
Bislang habe ich DBUnit-Tests immer auf einer MySql-Datenbank ausgeführt und möchte nun auf H2-Inmemory wechseln.
Das klappt noch nicht, daher beschreibe ich ersteinmal den Testaufbau mit MySql.

Konfiguration:
Code:
AnnotationConfiguration config = new AnnotationConfiguration()
                .setProperty("hibernate.dialect", dialect)
                .setProperty("hibernate.connection.driver_class", driver)
                .setProperty("hibernate.connection.url", connectionPath + databaseName)
                .setProperty("hibernate.connection.username", username)
                .setProperty("hibernate.connection.password", password)
                .setProperty("hibernate.connection.charSet", "UTF-8")
                .setProperty("hibernate.cache.provider_class", "org.hibernate.cache.HashtableCacheProvider")
                .setProperty("hibernate.hbm2ddl.auto", autoStrategy.getName());


Um etwas zu testen, mache ich:
- Session aus bereits erstellter SessionFactory öffnen via openSession()
- z.B. DAO testen
- StatelessSession öffnen
- Daten via StatelessSession nachprüfen via openStatelessSession()
- beide Sessions schließen

Für die H2-DB habe ich die Konfiguration angepasst:

Code:
AnnotationConfiguration config = new AnnotationConfiguration()
                .setProperty("hibernate.dialect", dialect)
                .setProperty("hibernate.connection.driver_class", driver)
                .setProperty("hibernate.connection.url", connectionPath + databaseName)
                .setProperty("hibernate.connection.username", username)
                .setProperty("hibernate.connection.password", password)
                .setProperty("hibernate.connection.charSet", "UTF-8")
                .setProperty("hibernate.cache.provider_class", "org.hibernate.cache.HashtableCacheProvider")
                .setProperty("hibernate.hbm2ddl.auto", autoStrategy.getName())
                .setProperty("hibernate.current_session_context_class", "thread")


Wenn ich nun aber meine Tests ablaufen lassen, gibt es bei Nutzung der jeweils zweiten Session
einen Timeout aufgrund eines Locks.

Code:
org.hibernate.exception.GenericJDBCException: could not load an entity: [TestTable#1]
...
Caused by: org.h2.jdbc.JdbcSQLException: Zeitüberschreitung beim Versuch die Tabelle TestTable zu sperren
...
Timeout trying to lock table TestTable [50200-67]


Es hat ein wenig gedauert, bis ich bemerkt habe, dass es generell an der zweiten Session liegt. Meine Vermutung ist
daher, dass der Sessionpool nur eine herausrückt, aber keine zweite liefern kann (obwohl der Fehler nach etwas anderem
klingt ..). Nutze ich nämlich getCurrentSession() (gebunden an den jeweiligen Thread), funktioniert es tadellos.

Daher habe ich versucht, den Pool zu konfigurieren:

Code:
.setProperty("c3p0.acquire_increment", "1")
.setProperty("c3p0.idle_test_period", "100")
.setProperty("c3p0.max_size", "100")
.setProperty("c3p0.max_statements", "0")
.setProperty("c3p0.min_size", "10")
.setProperty("c3p0.timeout", "100");


Scheinbar habe ich hier aber etwas vergessen, denn dies hat keine Auswirkung auf die Verbindung (oder ich irre mich doch!),
denn der Timeout kommt noch immer nach etwa 1sek - ich hatte erwartet, dass das Problem sich löst oder zumindest ein Timeout
von 100sek angesetzt wird.

Weiß jemand, woran es liegt?
Auch würde mich interessieren, warum das Problem bei einer MySql Konfiguration nicht auftaucht.


Top
 Profile  
 
 Post subject: Re: Hibernate + H2 DB: Öffnen einer zweiten Session => Timeout
PostPosted: Thu Mar 11, 2010 8:43 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Quote:
Caused by: org.h2.jdbc.JdbcSQLException: Zeitüberschreitung beim Versuch die Tabelle TestTable zu sperren
...
Timeout trying to lock table TestTable [50200-67]


Du versuchst gerade die Tabelle TestTable zu sperren, diese Tabelle wird aber noch von einer anderen Session gesperrt.
Das hat mit SessionPool-konfiguration nichts zu tun.
Es waere interessant den gesamten Stacktrace der Exception zu sehen,
denn mich wundert ein wenig, wer diese Tabelle explizit sperrt.


Top
 Profile  
 
 Post subject: Re: Hibernate + H2 DB: Öffnen einer zweiten Session => Timeout
PostPosted: Thu Mar 11, 2010 9:40 am 
Beginner
Beginner

Joined: Thu Oct 04, 2007 12:22 pm
Posts: 48
Danke für Deine Antwort. Ich bin davon ausgegangen, dass es was mit dem Pool zu tun hat, da die Arbeit
über getCurrentSession(), also in dem Fall genau die gleiche Session, funktioniert.

Aber der volle StackTrace:

Code:
org.hibernate.exception.GenericJDBCException: could not load an entity: [TestTable#1]
   at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
   at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
   at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
   at org.hibernate.loader.Loader.loadEntity(Loader.java:1874)
   at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
   at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
   at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3042)
   at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
   at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
   at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
   at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:98)
   at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
   at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:836)
   at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:66)
   at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
   at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
   at test.TestTable$$EnhancerByCGLIB$$212e343e.toString(<generated>)
   at java.lang.String.valueOf(String.java:2826)
   at java.io.PrintStream.println(PrintStream.java:771)
   at test.ExampleTest.foo(ExampleTest.java:57)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
   at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
   at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
   at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
   at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
   at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
   at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
   at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
   at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
   at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
   at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.h2.jdbc.JdbcSQLException: Zeitüberschreitung beim Versuch die Tabelle TestTable zu sperren
Timeout trying to lock table TestTable [50200-67]
   at org.h2.message.Message.getSQLException(Message.java:89)
   at org.h2.message.Message.getSQLException(Message.java:93)
   at org.h2.message.Message.getSQLException(Message.java:71)
   at org.h2.table.TableData.lock(TableData.java:380)
   at org.h2.table.TableFilter.lock(TableFilter.java:82)
   at org.h2.command.dml.Select.queryWithoutCache(Select.java:378)
   at org.h2.command.dml.Query.query(Query.java:227)
   at org.h2.command.CommandContainer.query(CommandContainer.java:77)
   at org.h2.command.Command.executeQueryLocal(Command.java:118)
   at org.h2.command.Command.executeQuery(Command.java:106)
   at org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:89)
   at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
   at org.hibernate.loader.Loader.getResultSet(Loader.java:1787)
   at org.hibernate.loader.Loader.doQuery(Loader.java:674)
   at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
   at org.hibernate.loader.Loader.loadEntity(Loader.java:1860)
   ... 40 more


Top
 Profile  
 
 Post subject: Re: Hibernate + H2 DB: Öffnen einer zweiten Session => Timeout
PostPosted: Thu Mar 11, 2010 10:42 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Quote:
at org.h2.table.TableData.lock(TableData.java:380)
at org.h2.table.TableFilter.lock(TableFilter.java:82)
at org.h2.command.dml.Select.queryWithoutCache(Select.java:378)
at org.h2.command.dml.Query.query(Query.java:227)
at org.h2.command.CommandContainer.query(CommandContainer.java:77)
at org.h2.command.Command.executeQueryLocal(Command.java:118)
at org.h2.command.Command.executeQuery(Command.java:106)
at org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:89)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1787)
at org.hibernate.loader.Loader.doQuery(Loader.java:674)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1860)


Dieser Teil von Stacktrace ist sehr komisch:
eine normale loadEntity (= read-only) Aktion fuehrt anscheinend dazu, dass der H2-JDBC-driver eine Tabelle lockt.
Was ist denn der Standard isolation level von H2 DB ?
Viellecht musst Du einen anderen isolation level auswaehlen um das Problem zu loesen.
Auf jedem Fall wuerde ich mich mal in die Doku von H2 DB und speziell in der des H2-JDBC-drivers einlesen.
Es ist mehr ein H2-JDBC-driver problem als ein Hibernate-Problem.


Top
 Profile  
 
 Post subject: Re: Hibernate + H2 DB: Öffnen einer zweiten Session => Timeout
PostPosted: Thu Mar 11, 2010 12:33 pm 
Beginner
Beginner

Joined: Thu Oct 04, 2007 12:22 pm
Posts: 48
Ja, da hatte ich bereits geschaut, es gibt folgenden Hinweis:

http://www.h2database.com/html/advanced.html

dort steht etwas, das in Foren bei meinem Problem manchmal als Option empfohlen wird:
Quote:
Multi-Version Concurrency Control (MVCC)

The MVCC feature allows higher concurrency than using (table level or row level) locks. When using MVCC in this database, delete, insert and update operations will only issue a shared lock on the table. An exclusive lock is still used when adding or removing columns, when dropping the table, and when using SELECT ... FOR UPDATE. Connections only 'see' committed data, and own changes. That means, if connection A updates a row but doesn't commit this change yet, connection B will see the old value. Only when the change is committed, the new value is visible by other connections (read committed). If multiple connections concurrently try to update the same row, the database waits until it can apply the change, but at most until the lock timeout expires.

To use the MVCC feature, append ;MVCC=TRUE to the database URL:

jdbc:h2:~/test;MVCC=TRUE

The MVCC feature is not fully tested yet. The limitations of the MVCC mode are: it can not be used at the same time as MULTI_THREADED=TRUE; the complete undo log must fit in memory when using multi-version concurrency (the setting MAX_MEMORY_UNDO has no effect).


So ganz schlüssig ist dieser Modus in meinem Fall nicht für mich. Ich hatte erfolglos rumprobiert, es könnte aber auch an einer ganz trivialen Sache liegen, dass ich
damit nichts erreichte: Wie konfiguriere ich die Properties der Hibernate-Configuration so, dass er (komplett!) nach dem ConnectionString das ";MVCC=True" setzt?


Top
 Profile  
 
 Post subject: Re: Hibernate + H2 DB: Öffnen einer zweiten Session => Timeout
PostPosted: Fri Mar 12, 2010 3:22 am 
Expert
Expert

Joined: Tue Jun 16, 2009 3:36 am
Posts: 990
Quote:
Wie konfiguriere ich die Properties der Hibernate-Configuration so, dass er (komplett!) nach dem ConnectionString das ";MVCC=True" setzt?


So sollte es eigentlich funktionieren:
Code:
.setProperty("hibernate.connection.url", connectionPath + databaseName + ";MVCC=True")


Zudem wuerde ich versuchen herausfinden ob es sich bei dem bestehenden Lock um ein Shared lock oder ein exclusive lock handelt.
Letzterer Fall wuede bedeuten, dass Du mit der 2. session (der stateless) versuchst die Daten zu lesen,
bevor Du die Transaktion der ersten session commited hast (mit dem Commit sollte naemlich der exclusive lock aufgeloest werden).



Quote:
Table Level Locking

The database allows multiple concurrent connections to the same database. To make sure all connections only see consistent data, table level locking is used by default. This mechanism does not allow high concurrency, but is very fast. Shared locks and exclusive locks are supported. Before reading from a table, the database tries to add a shared lock to the table (this is only possible if there is no exclusive lock on the object by another connection). If the shared lock is added successfully, the table can be read. It is allowed that other connections also have a shared lock on the same object. If a connection wants to write to a table (update or delete a row), an exclusive lock is required. To get the exclusive lock, other connection must not have any locks on the object. After the connection commits, all locks are released. This database keeps all locks in memory.


Top
 Profile  
 
 Post subject: Re: Hibernate + H2 DB: Öffnen einer zweiten Session => Timeout
PostPosted: Mon Mar 15, 2010 9:47 am 
Beginner
Beginner

Joined: Thu Oct 04, 2007 12:22 pm
Posts: 48
Vielen Dank für Deine Mühe, letztendlich hast Du den richtigen Hinweis gegeben. In meinen Tests, die direkt DAOs prüfen, verwende ich keine Transaktionen. Ich habe darin keinen Sinn gesehen, da auf dieser Ebene transaktionale Zusammenhänge (und deren Sicherheit) keine Rolle spielen und im Produktivcode von einer höheren Ebene bestimmt würden. Im Gegensatz zum MySQL Treiber hat H2 damit aber tatsächlich ein Problem.
Der Sinn dahiner bleibt mir zwar verschlossen - immerhin bedeutet keine explizite Transaktion zu nutzen ja, dass die Transationen auf Statement-Ebene sind - aber so funktioniert es.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.