Author: sannegrinovero
Date: 2009-04-07 16:49:32 -0400 (Tue, 07 Apr 2009)
New Revision: 16272
Added:
search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusLine.java
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusStop.java
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/LazyCollectionsUpdatingTest.java
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/RollbackTransactionTest.java
Modified:
search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/EventSourceTransactionContext.java
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java
search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java
search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java
search/branches/Branch_3_1/src/test/org/hibernate/search/test/classloading/NoAnnotationsTest.java
Log:
HSEARCH-178 for 3.1 branch
Modified: search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml
===================================================================
--- search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml 2009-04-07
20:20:12 UTC (rev 16271)
+++ search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml 2009-04-07
20:49:32 UTC (rev 16272)
@@ -600,7 +600,8 @@
<para>To enable Hibernate Search in Hibernate Core (ie. if you don't use
Hibernate Annotations), add the
<literal>FullTextIndexEventListener</literal> for the following six
- Hibernate events.</para>
+ Hibernate events and add the
<literal>IndexWorkFlushEventListener</literal> after
+ the default <literal>DefaultFlushEventListener</literal>, as in the
following example.</para>
<example>
<title>Explicitly enabling Hibernate Search by configuring the
@@ -609,24 +610,28 @@
<programlisting><hibernate-configuration>
<session-factory>
...
- <event type="post-update"/>
+ <event type="post-update">
<listener
class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>
- <event type="post-insert"/>
+ <event type="post-insert">
<listener
class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>
- <event type="post-delete"/>
+ <event type="post-delete">
<listener
class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>
- <event type="post-collection-recreate"/>
+ <event type="post-collection-recreate">
<listener
class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>
- <event type="post-collection-remove"/>
+ <event type="post-collection-remove">
<listener
class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>
- <event type="post-collection-update"/>
+ <event type="post-collection-update">
<listener
class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>
+ <event type="flush">
+ <listener
class="org.hibernate.event.def.DefaultFlushEventListener"/>
+ <listener
class="org.hibernate.search.event.IndexWorkFlushEventListener"/>
+ </event>
</session-factory>
</hibernate-configuration></programlisting>
</example>
Modified:
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/EventSourceTransactionContext.java
===================================================================
---
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/EventSourceTransactionContext.java 2009-04-07
20:20:12 UTC (rev 16271)
+++
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/EventSourceTransactionContext.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -2,35 +2,93 @@
package org.hibernate.search.backend.impl;
import java.io.Serializable;
+
import javax.transaction.Synchronization;
+import org.hibernate.AssertionFailure;
import org.hibernate.Transaction;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.FlushEventListener;
import org.hibernate.search.backend.TransactionContext;
-import org.hibernate.event.EventSource;
+import org.hibernate.search.event.IndexWorkFlushEventListener;
+import org.hibernate.search.util.LoggerFactory;
+import org.slf4j.Logger;
/**
* Implementation of the transactional context on top of an EventSource (Session)
*
* @author Navin Surtani - navin(a)surtani.org
+ * @author Emmanuel Bernard
+ * @author Sanne Grinovero
*/
public class EventSourceTransactionContext implements TransactionContext, Serializable {
- EventSource eventSource;
+
+ private static final Logger log = LoggerFactory.make();
+
+ private final EventSource eventSource;
+ private final IndexWorkFlushEventListener flushListener;
+ //constructor time is too early to define the value of realTxInProgress,
+ //postpone it, otherwise doing
+ // " openSession - beginTransaction "
+ //will behave as "out of transaction" in the whole session lifespan.
+ private Boolean realTxInProgress = null;
+
public EventSourceTransactionContext(EventSource eventSource) {
this.eventSource = eventSource;
+ this.flushListener = findIndexWorkFlushEventListener();
}
public Object getTransactionIdentifier() {
- return eventSource.getTransaction();
+ if ( isRealTransactionInProgress() ) {
+ return eventSource.getTransaction();
+ }
+ else {
+ return eventSource;
+ }
}
public void registerSynchronization(Synchronization synchronization) {
- Transaction transaction = eventSource.getTransaction();
- transaction.registerSynchronization( synchronization );
+ if ( isRealTransactionInProgress() ) {
+ Transaction transaction = eventSource.getTransaction();
+ transaction.registerSynchronization( synchronization );
+ }
+ else {
+ if ( flushListener != null ) {
+ flushListener.addSynchronization( eventSource, synchronization );
+ }
+ else {
+ //It appears we are flushing out of transaction and have no way to perform the index
update
+ //Not expected: see check in isTransactionInProgress()
+ throw new AssertionFailure( "On flush out of transaction:
IndexWorkFlushEventListener not registered" );
+ }
+ }
}
+
+ private IndexWorkFlushEventListener findIndexWorkFlushEventListener() {
+ FlushEventListener[] flushEventListeners =
eventSource.getListeners().getFlushEventListeners();
+ for (FlushEventListener listener : flushEventListeners) {
+ if ( listener.getClass().equals( IndexWorkFlushEventListener.class ) ) {
+ return (IndexWorkFlushEventListener) listener;
+ }
+ }
+ log.debug( "No IndexWorkFlushEventListener was registered" );
+ return null;
+ }
+ //The code is not really fitting the method name;
+ //(unless you consider a flush as a mini-transaction)
+ //This is because we want to behave as "inTransaction" if the flushListener is
registered.
public boolean isTransactionInProgress() {
- return eventSource.isTransactionInProgress();
+ // either it is a real transaction, or if we are capable to manage this in the
IndexWorkFlushEventListener
+ return isRealTransactionInProgress() || flushListener != null;
}
-
+
+ private boolean isRealTransactionInProgress() {
+ if ( realTxInProgress == null ) {
+ realTxInProgress = eventSource.isTransactionInProgress();
+ }
+ return realTxInProgress;
+ }
+
}
Modified:
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java
===================================================================
---
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java 2009-04-07
20:20:12 UTC (rev 16271)
+++
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -1,71 +1,83 @@
-//$Id$
-package org.hibernate.search.backend.impl;
-
-import java.util.Properties;
-
-import javax.transaction.Synchronization;
-
-import org.hibernate.search.backend.QueueingProcessor;
-import org.hibernate.search.backend.Work;
-import org.hibernate.search.backend.WorkQueue;
-import org.hibernate.search.backend.Worker;
-import org.hibernate.search.backend.TransactionContext;
-import org.hibernate.search.engine.SearchFactoryImplementor;
-import org.hibernate.search.util.WeakIdentityHashMap;
-
-/**
- * Queue works per transaction.
- * If out of transaction, the work is executed right away
- * <p/>
- * When <code>hibernate.search.worker.type</code> is set to
<code>async</code>
- * the work is done in a separate thread (threads are pooled)
- *
- * @author Emmanuel Bernard
- */
-public class TransactionalWorker implements Worker {
- //not a synchronized map since for a given transaction, we have not concurrent access
- protected final WeakIdentityHashMap<Object, Synchronization>
synchronizationPerTransaction = new WeakIdentityHashMap<Object, Synchronization>();
- private QueueingProcessor queueingProcessor;
-
- public void performWork(Work work, TransactionContext transactionContext) {
- if ( transactionContext.isTransactionInProgress() ) {
- Object transaction = transactionContext.getTransactionIdentifier();
- PostTransactionWorkQueueSynchronization txSync = (
PostTransactionWorkQueueSynchronization )
- synchronizationPerTransaction.get( transaction );
- if ( txSync == null || txSync.isConsumed() ) {
- txSync = new PostTransactionWorkQueueSynchronization(
- queueingProcessor, synchronizationPerTransaction
- );
- transactionContext.registerSynchronization( txSync );
- synchronizationPerTransaction.put( transaction, txSync );
- }
- txSync.add( work );
- }
- else {
- WorkQueue queue = new WorkQueue( 2 ); //one work can be split
- queueingProcessor.add( work, queue );
- queueingProcessor.prepareWorks( queue );
- queueingProcessor.performWorks( queue );
- }
- }
-
- public void initialize(Properties props, SearchFactoryImplementor searchFactory) {
- this.queueingProcessor = new BatchedQueueingProcessor( searchFactory, props );
- }
-
- public void close() {
- queueingProcessor.close();
- }
-
- public void flushWorks(TransactionContext transactionContext) {
- if ( transactionContext.isTransactionInProgress() ) {
- Object transaction = transactionContext.getTransactionIdentifier();
- PostTransactionWorkQueueSynchronization txSync = (
PostTransactionWorkQueueSynchronization )
- synchronizationPerTransaction.get( transaction );
- if ( txSync != null && !txSync.isConsumed() ) {
- txSync.flushWorks();
- }
- }
- }
-
-}
+//$Id$
+package org.hibernate.search.backend.impl;
+
+import java.util.Properties;
+
+import javax.transaction.Synchronization;
+
+import org.hibernate.search.backend.QueueingProcessor;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.backend.WorkQueue;
+import org.hibernate.search.backend.Worker;
+import org.hibernate.search.backend.TransactionContext;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.util.LoggerFactory;
+import org.hibernate.search.util.WeakIdentityHashMap;
+import org.slf4j.Logger;
+
+/**
+ * Queue works per transaction.
+ * If out of transaction, the work is executed right away
+ * <p/>
+ * When <code>hibernate.search.worker.type</code> is set to
<code>async</code>
+ * the work is done in a separate thread (threads are pooled)
+ *
+ * @author Emmanuel Bernard
+ */
+public class TransactionalWorker implements Worker {
+
+ //note: there is one Worker instance per SearchFactory, reused concurrently for all
sessions.
+
+ private static final Logger log = LoggerFactory.make();
+
+ //FIXME: discuss the next line! it looks like there actually is concurrent access
+ //not a synchronized map since for a given transaction, we have not concurrent access
+ protected final WeakIdentityHashMap<Object, Synchronization>
synchronizationPerTransaction = new WeakIdentityHashMap<Object, Synchronization>();
+ private QueueingProcessor queueingProcessor;
+
+ public void performWork(Work work, TransactionContext transactionContext) {
+ if ( transactionContext.isTransactionInProgress() ) {
+ Object transactionIdentifier = transactionContext.getTransactionIdentifier();
+ PostTransactionWorkQueueSynchronization txSync = (
PostTransactionWorkQueueSynchronization )
+ synchronizationPerTransaction.get( transactionIdentifier );
+ if ( txSync == null || txSync.isConsumed() ) {
+ txSync = new PostTransactionWorkQueueSynchronization(
+ queueingProcessor, synchronizationPerTransaction
+ );
+ transactionContext.registerSynchronization( txSync );
+ synchronizationPerTransaction.put( transactionIdentifier, txSync );
+ }
+ txSync.add( work );
+ }
+ else {
+ // this is a workaround: isTransactionInProgress should return "true"
+ // for correct configurations.
+ log.warn( "It appears changes are being pushed to the index out of a transaction.
" +
+ "Register the IndexWorkFlushEventListener listener on flush to correctly manage
Collections!" );
+ WorkQueue queue = new WorkQueue( 2 ); //one work can be split
+ queueingProcessor.add( work, queue );
+ queueingProcessor.prepareWorks( queue );
+ queueingProcessor.performWorks( queue );
+ }
+ }
+
+ public void initialize(Properties props, SearchFactoryImplementor searchFactory) {
+ this.queueingProcessor = new BatchedQueueingProcessor( searchFactory, props );
+ }
+
+ public void close() {
+ queueingProcessor.close();
+ }
+
+ public void flushWorks(TransactionContext transactionContext) {
+ if ( transactionContext.isTransactionInProgress() ) {
+ Object transaction = transactionContext.getTransactionIdentifier();
+ PostTransactionWorkQueueSynchronization txSync = (
PostTransactionWorkQueueSynchronization )
+ synchronizationPerTransaction.get( transaction );
+ if ( txSync != null && !txSync.isConsumed() ) {
+ txSync.flushWorks();
+ }
+ }
+ }
+
+}
Modified:
search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java
===================================================================
---
search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java 2009-04-07
20:20:12 UTC (rev 16271)
+++
search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -94,7 +94,13 @@
new PostCollectionUpdateEventListener[] { searchListener }
)
);
-
+ // Adding IndexWorkFlushEventListener to manage events out-of-transaction
+ if ( ! isFlushEventListenerRegistered( listeners.getFlushEventListeners() ) ) {
+ listeners.setFlushEventListeners( appendToArray(
+ listeners.getFlushEventListeners(),
+ new IndexWorkFlushEventListener()
+ ) );
+ }
}
/**
@@ -159,4 +165,19 @@
}
return false;
}
+
+ /**
+ * Verifies if an IndexWorkFlushEventListener is contained in the array of listeners.
+ * @param listeners
+ * @return true if it found in the listeners, false otherwise.
+ */
+ private static boolean isFlushEventListenerRegistered(Object[] listeners) {
+ for ( Object eventListener : listeners ) {
+ if ( IndexWorkFlushEventListener.class == eventListener.getClass() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
Added:
search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java
===================================================================
---
search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java
(rev 0)
+++
search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -0,0 +1,61 @@
+// $Id$
+package org.hibernate.search.event;
+
+import java.io.Serializable;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.FlushEvent;
+import org.hibernate.event.FlushEventListener;
+import org.hibernate.search.util.LoggerFactory;
+import org.slf4j.Logger;
+
+/**
+ * FlushEventListener to make sure the indexes are updated right after the hibernate
flush,
+ * avoiding object loading during a flush. Not needed during transactions.
+ *
+ * @author Sanne Grinovero
+ */
+public final class IndexWorkFlushEventListener implements FlushEventListener,
Serializable {
+
+ private static final Logger log = LoggerFactory.make();
+
+ private final ConcurrentHashMap<Session, Synchronization>
synchronizationPerTransaction
+ = new ConcurrentHashMap<Session, Synchronization>();
+
+ public IndexWorkFlushEventListener() {
+ }
+
+ public void onFlush(FlushEvent event) throws HibernateException {
+ Session session = event.getSession();
+ Synchronization synchronization = synchronizationPerTransaction.get( session );
+ if ( synchronization != null ) {
+ log.debug( "flush event causing index update out of transaction" );
+ synchronizationPerTransaction.remove( session );
+ synchronization.beforeCompletion();
+ synchronization.afterCompletion( Status.STATUS_COMMITTED );
+ }
+ }
+
+ public void addSynchronization(EventSource eventSource, Synchronization synchronization)
{
+ Synchronization previousSync = synchronizationPerTransaction.put( eventSource,
synchronization );
+ if ( previousSync != null ) {
+ throw new AssertionFailure( "previous registered sync not discarded in
IndexWorkFlushEventListener" );
+ }
+ }
+
+ /*
+ * Might want to implement AutoFlushEventListener in future?
+ public void onAutoFlush(AutoFlushEvent event) throws HibernateException {
+ // Currently not needed as auto-flush is not happening
+ // when out of transaction.
+ }
+ */
+
+}
Property changes on:
search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java
===================================================================
--- search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java 2009-04-07
20:20:12 UTC (rev 16271)
+++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -13,6 +13,9 @@
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
+import org.hibernate.event.FlushEventListener;
+import org.hibernate.event.def.DefaultFlushEventListener;
+import org.hibernate.search.event.IndexWorkFlushEventListener;
/**
* A modified base class for tests without annotations.
@@ -143,9 +146,15 @@
}
protected void configure(Configuration cfg) {
+ //needs to register all event listeners:
cfg.setListener( "post-update",
"org.hibernate.search.event.FullTextIndexEventListener" );
cfg.setListener( "post-insert",
"org.hibernate.search.event.FullTextIndexEventListener" );
cfg.setListener( "post-delete",
"org.hibernate.search.event.FullTextIndexEventListener" );
+ cfg.setListener( "post-collection-recreate",
"org.hibernate.search.event.FullTextIndexEventListener" );
+ cfg.setListener( "post-collection-remove",
"org.hibernate.search.event.FullTextIndexEventListener" );
+ cfg.setListener( "post-collection-update",
"org.hibernate.search.event.FullTextIndexEventListener" );
+
+ cfg.setListeners( "flush", new FlushEventListener[]{new
DefaultFlushEventListener(), new IndexWorkFlushEventListener()} );
cfg.setProperty( "hibernate.search.default.directory_provider",
RAMDirectoryProvider.class.getName() );
cfg.setProperty( org.hibernate.search.Environment.ANALYZER_CLASS,
StopAnalyzer.class.getName() );
Modified:
search/branches/Branch_3_1/src/test/org/hibernate/search/test/classloading/NoAnnotationsTest.java
===================================================================
---
search/branches/Branch_3_1/src/test/org/hibernate/search/test/classloading/NoAnnotationsTest.java 2009-04-07
20:20:12 UTC (rev 16271)
+++
search/branches/Branch_3_1/src/test/org/hibernate/search/test/classloading/NoAnnotationsTest.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -10,7 +10,6 @@
import org.hibernate.Transaction;
import org.hibernate.search.Search;
-
/**
* @author Hardy Ferentschik
*/
@@ -39,7 +38,28 @@
tx.commit();
s.close();
}
+
+ public void testFlushListenerRegistrationWithoutAnnotations() throws Exception {
+ // This test should pass even if the flushListener is not registered,
+ // as a workaround is done in code (you'll see a warning in logs).
+ Animal pinguin = new Animal();
+ pinguin.setName( "Penguin" );
+ Session s = openSession();
+ s.save( pinguin );
+ s.flush();
+ s.clear();
+
+ Transaction tx = s.beginTransaction();
+ tx = s.beginTransaction();
+ List results = Search.getFullTextSession( s ).createFullTextQuery(
+ new TermQuery( new Term( "name", "penguin" ) )
+ ).list();
+ assertEquals( 1, results.size() );
+ tx.commit();
+ s.close();
+ }
+
protected String[] getXmlFiles() {
return new String[] {
"org/hibernate/search/test/classloading/Animal.hbm.xml"
Added: search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusLine.java
===================================================================
--- search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusLine.java
(rev 0)
+++
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusLine.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -0,0 +1,87 @@
+// $Id$
+package org.hibernate.search.test.engine;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.IndexedEmbedded;
+import org.hibernate.search.annotations.Store;
+
+/**
+ * Test entity: BusLine have many BusStops: needed to verify
+ * indexing of a lazy-loaded collection in out-of-transaction use case.
+ *
+ * @author Sanne Grinovero
+ */
+@Entity
+@Indexed
+public class BusLine {
+
+ private Long id;
+ private String busLineName;
+ private Set<BusStop> stops = new HashSet<BusStop>();
+
+ @Id
+ @GeneratedValue
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @Field(index=Index.NO,store=Store.YES)
+ public String getBusLineName() {
+ return busLineName;
+ }
+
+ public void setBusLineName(String busLine) {
+ this.busLineName = busLine;
+ }
+
+ @ManyToMany(cascade=CascadeType.PERSIST)
+ @IndexedEmbedded
+ public Set<BusStop> getStops() {
+ return stops;
+ }
+
+ public void setStops(Set<BusStop> stops) {
+ this.stops = stops;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((busLineName == null) ? 0 : busLineName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BusLine other = (BusLine) obj;
+ if (busLineName == null) {
+ if (other.busLineName != null)
+ return false;
+ } else if (!busLineName.equals(other.busLineName))
+ return false;
+ return true;
+ }
+
+}
Added: search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusStop.java
===================================================================
--- search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusStop.java
(rev 0)
+++
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusStop.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -0,0 +1,82 @@
+// $Id$
+package org.hibernate.search.test.engine;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.Field;
+
+/**
+ * Test entity: BusStop is @ContainedIn BusLine
+ *
+ * @author Sanne Grinovero
+ */
+@Entity
+public class BusStop {
+
+ private Long id;
+ private String roadName;
+ private Set<BusLine> busses = new HashSet<BusLine>();
+
+ @Id
+ @GeneratedValue
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @Field
+ public String getRoadName() {
+ return roadName;
+ }
+
+ public void setRoadName(String roadName) {
+ this.roadName = roadName;
+ }
+
+ @ManyToMany(mappedBy="stops")
+ @ContainedIn
+ public Set<BusLine> getBusses() {
+ return busses;
+ }
+
+ public void setBusses(Set<BusLine> busses) {
+ this.busses = busses;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((roadName == null) ? 0 : roadName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BusStop other = (BusStop) obj;
+ if (roadName == null) {
+ if (other.roadName != null)
+ return false;
+ } else if (!roadName.equals(other.roadName))
+ return false;
+ return true;
+ }
+
+}
Property changes on:
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/BusStop.java
___________________________________________________________________
Name: svn:keywords
+ Id
Added:
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/LazyCollectionsUpdatingTest.java
===================================================================
---
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/LazyCollectionsUpdatingTest.java
(rev 0)
+++
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/LazyCollectionsUpdatingTest.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -0,0 +1,119 @@
+// $Id$
+package org.hibernate.search.test.engine;
+
+import java.util.List;
+
+import org.apache.lucene.analysis.SimpleAnalyzer;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.TermQuery;
+import org.hibernate.Transaction;
+import org.hibernate.search.Environment;
+import org.hibernate.search.FullTextQuery;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.store.RAMDirectoryProvider;
+import org.hibernate.search.test.SearchTestCase;
+
+/**
+ * TestCase for HSEARCH-178 (Search hitting HHH-2763)
+ * Verifies that it's possible to index lazy loaded collections from
+ * indexed entities even when no transactions are used.
+ *
+ * @author Sanne Grinovero
+ */
+public class LazyCollectionsUpdatingTest extends SearchTestCase {
+
+ public void testUpdatingInTransaction() {
+ assertFindsByRoadName( "buonarroti" );
+ FullTextSession fullTextSession = Search.getFullTextSession( sessions.openSession() );
+ try {
+ Transaction tx = fullTextSession.beginTransaction();
+ BusStop busStop = (BusStop) fullTextSession.get( BusStop.class, 1L );
+ busStop.setRoadName( "new road" );
+ tx.commit();
+ }
+ catch (org.hibernate.AssertionFailure ass) {
+ fail( ass.getMessage() );
+ }
+ finally {
+ fullTextSession.close();
+ }
+ assertFindsByRoadName( "new" );
+ }
+
+ public void testUpdatingOutOfTransaction() {
+ assertFindsByRoadName( "buonarroti" );
+ FullTextSession fullTextSession = Search.getFullTextSession( sessions.openSession() );
+ try {
+ BusStop busStop = (BusStop) fullTextSession.get( BusStop.class, 1L );
+ busStop.setRoadName( "new road" );
+ fullTextSession.flush();
+ }
+ catch (org.hibernate.AssertionFailure ass) {
+ fail( ass.getMessage() );
+ }
+ finally {
+ fullTextSession.close();
+ }
+ assertFindsByRoadName( "new" );
+ }
+
+ public void assertFindsByRoadName(String analyzedRoadname) {
+ FullTextSession fullTextSession = Search.getFullTextSession( sessions.openSession() );
+ Transaction tx = fullTextSession.beginTransaction();
+ TermQuery ftQuery = new TermQuery( new Term( "stops.roadName",
analyzedRoadname ) );
+ FullTextQuery query = fullTextSession.createFullTextQuery( ftQuery, BusLine.class );
+ query.setProjection( "busLineName" );
+ assertEquals( 1, query.list().size() );
+ List results = query.list();
+ String resultName = (String) ((Object[])results.get(0))[0];
+ assertEquals( "Linea 64", resultName );
+ tx.commit();
+ fullTextSession.close();
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BusLine bus = new BusLine();
+ bus.setBusLineName( "Linea 64" );
+ addBusStop( bus, "Stazione Termini" );
+ addBusStop( bus, "via Gregorio VII" );
+ addBusStop( bus, "via Alessandro III" );
+ addBusStop( bus, "via M.Buonarroti" );
+ session.persist( bus );
+ tx.commit();
+ } catch (Throwable t) {
+ if ( tx != null )
+ tx.rollback();
+ } finally {
+ session.close();
+ }
+ }
+
+ private void addBusStop(BusLine bus, String roadName) {
+ BusStop stop = new BusStop();
+ stop.setRoadName( roadName );
+ bus.getStops().add( stop );
+ stop.getBusses().add( bus );
+ }
+
+ // Test setup options - Entities
+ @Override
+ protected Class[] getMappings() {
+ return new Class[] { BusLine.class, BusStop.class };
+ }
+
+ // Test setup options - SessionFactory Properties
+ @Override
+ protected void configure(org.hibernate.cfg.Configuration configuration) {
+ super.configure( configuration );
+ cfg.setProperty( "hibernate.search.default.directory_provider",
RAMDirectoryProvider.class.getName() );
+ cfg.setProperty( Environment.ANALYZER_CLASS, SimpleAnalyzer.class.getName() );
+ }
+
+}
Property changes on:
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/LazyCollectionsUpdatingTest.java
___________________________________________________________________
Name: svn:keywords
+ Id
Added:
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/RollbackTransactionTest.java
===================================================================
---
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/RollbackTransactionTest.java
(rev 0)
+++
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/RollbackTransactionTest.java 2009-04-07
20:49:32 UTC (rev 16272)
@@ -0,0 +1,90 @@
+// $Id$
+package org.hibernate.search.test.engine;
+
+import org.apache.lucene.analysis.SimpleAnalyzer;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.hibernate.Transaction;
+import org.hibernate.search.Environment;
+import org.hibernate.search.FullTextQuery;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.store.RAMDirectoryProvider;
+import org.hibernate.search.test.SearchTestCase;
+
+/**
+ * Verify index changes queued during a transaction are canceled
+ * when the transaction is rolled back.
+ *
+ * @author Sanne Grinovero
+ */
+public class RollbackTransactionTest extends SearchTestCase {
+
+ public void testTransactionBehaviour() {
+ assertEquals( 0, countBusLinesByFullText() );
+ assertEquals( 0, countBusLineByDatabaseCount() );
+ createBusLines( 5, true );
+ assertEquals( 0, countBusLinesByFullText() );
+ assertEquals( 0, countBusLineByDatabaseCount() );
+ createBusLines( 5, false );
+ assertEquals( 5, countBusLinesByFullText() );
+ assertEquals( 5, countBusLineByDatabaseCount() );
+ createBusLines( 7, true );
+ assertEquals( 5, countBusLinesByFullText() );
+ assertEquals( 5, countBusLineByDatabaseCount() );
+ createBusLines( 7, false );
+ assertEquals( 12, countBusLinesByFullText() );
+ assertEquals( 12, countBusLineByDatabaseCount() );
+ }
+
+ private void createBusLines(int number, boolean rollback) {
+ FullTextSession fullTextSession = Search.getFullTextSession( sessions.openSession() );
+ Transaction tx = fullTextSession.beginTransaction();
+ for (int i=0; i<number; i++ ) {
+ BusLine line = new BusLine();
+ line.setBusLineName( "line " + i );
+ fullTextSession.persist( line );
+ }
+ if ( rollback ) {
+ tx.rollback();
+ }
+ else {
+ tx.commit();
+ }
+ fullTextSession.close();
+ }
+
+ public int countBusLinesByFullText() {
+ FullTextSession fullTextSession = Search.getFullTextSession( sessions.openSession() );
+ Transaction tx = fullTextSession.beginTransaction();
+ org.apache.lucene.search.Query ftQuery = new MatchAllDocsQuery();
+ FullTextQuery query = fullTextSession.createFullTextQuery( ftQuery, BusLine.class );
+ int count = query.list().size();
+ tx.commit();
+ fullTextSession.close();
+ return count;
+ }
+
+ public int countBusLineByDatabaseCount() {
+ FullTextSession fullTextSession = Search.getFullTextSession( sessions.openSession() );
+ Transaction tx = fullTextSession.beginTransaction();
+ int count = fullTextSession.createCriteria( BusLine.class ).list().size();
+ tx.commit();
+ fullTextSession.close();
+ return count;
+ }
+
+ // Test setup options - Entities
+ @Override
+ protected Class[] getMappings() {
+ return new Class[] { BusLine.class, BusStop.class };
+ }
+
+ // Test setup options - SessionFactory Properties
+ @Override
+ protected void configure(org.hibernate.cfg.Configuration configuration) {
+ super.configure( configuration );
+ cfg.setProperty( "hibernate.search.default.directory_provider",
RAMDirectoryProvider.class.getName() );
+ cfg.setProperty( Environment.ANALYZER_CLASS, SimpleAnalyzer.class.getName() );
+ }
+
+}
Property changes on:
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/RollbackTransactionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id