/* * JBoss, Home of Professional Open Source * Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package org.hibernate.search.test.engine.optimizations; import java.lang.annotation.ElementType; import org.hibernate.Transaction; import org.hibernate.collection.internal.PersistentBag; import org.hibernate.collection.internal.PersistentSet; import org.hibernate.search.FullTextSession; import org.hibernate.search.cfg.EntityMapping; import org.hibernate.search.cfg.SearchMapping; import org.hibernate.search.test.util.FullTextSessionBuilder; import org.junit.Test; import static org.fest.assertions.Assertions.assertThat; /** * HSEARCH-679 - verify that updates to collections that are not indexed do not trigger indexing. * Updating a collection in an entity which is not indexed, but has some other property marked * as containedIn, should generally not trigger indexing of the containedIn objects, unless * we can't tell for sure the index state is not going to be affected. * * @author Sanne Grinovero * @author Tom Waterhouse */ public class CollectionUpdateEventTest { /** * If the top level class has a classbridge or dynamicboost, then we can't safely * enable this optimization. */ @Test public void testWithClassBridge() { testScenario( true, 2, false ); } /** * The indexing should be skipped when no custom bridges are used. */ @Test public void testWithoutClassBridge() { testScenario( false, 2, false ); } /** * Test having a depth which is not enough to reach the dirty collection. * We should be able to optimize this case. */ @Test public void testWithNoEnoughDepth() { testScenario( true, 1, false ); } /** * Test again with a no-enough-depth scenario, but having the classbridge * defined close to the collection (far from the root entity) */ @Test public void testWithDeepClassBridge() { testScenario( false, 1, true ); } private void testScenario(boolean usingClassBridge, int depth, boolean usingClassbridgeOnEmbedded) { FullTextSessionBuilder fulltextSessionBuilder = createSearchFactory( usingClassBridge, depth, usingClassbridgeOnEmbedded ); try { initializeData( fulltextSessionBuilder ); FullTextSession fullTextSession = fulltextSessionBuilder.openFullTextSession(); try { Catalog catalog = (Catalog) fullTextSession.get( Catalog.class, 1L ); PersistentSet catalogItems = (PersistentSet) catalog.getCatalogItems(); PersistentBag consumers = (PersistentBag) catalog.getConsumers(); assertThat( consumers.wasInitialized() ) .as( "consumers should not be initialized" ) .isFalse(); assertThat( consumers.wasInitialized() ) .as( "catalogItems should not be initialized" ) .isFalse(); updateCatalogsCollection( fullTextSession, catalog ); if ( ( usingClassBridge || usingClassbridgeOnEmbedded ) && depth > 1 ) { assertThat( catalogItems.wasInitialized() ) .as( "catalogItems should have been initialized" ) .isTrue(); } else { assertThat( catalogItems.wasInitialized() ) .as( "catalogItems should not be initialized" ) .isFalse(); } } finally { fullTextSession.close(); } } finally { fulltextSessionBuilder.close(); } } private FullTextSessionBuilder createSearchFactory(boolean defineClassBridge, int depth, boolean usingClassbridgeOnEmbedded) { FullTextSessionBuilder builder = new FullTextSessionBuilder() .addAnnotatedClass( Catalog.class ) .addAnnotatedClass( CatalogItem.class ) .addAnnotatedClass( Consumer.class ) .addAnnotatedClass( Item.class ); SearchMapping fluentMapping = builder.fluentMapping(); EntityMapping catalogMapping = fluentMapping .entity( Catalog.class ); if ( usingClassbridgeOnEmbedded ) { catalogMapping.classBridge( ItemClassBridge.class ); } catalogMapping .property( "catalogItems", ElementType.FIELD ).containedIn() .entity( CatalogItem.class ) .property( "item", ElementType.FIELD ).containedIn() .property( "catalog", ElementType.FIELD ).indexEmbedded(); if ( defineClassBridge ) { fluentMapping .entity( Item.class ) .classBridge( ItemClassBridge.class ) .indexed() .property( "catalogItems", ElementType.FIELD ).indexEmbedded().depth( depth ); } else { fluentMapping .entity( Item.class ) .indexed() .property( "catalogItems", ElementType.FIELD ).indexEmbedded().depth( depth ); } return builder.build(); } /** * Initialize the test data. * @param fulltextSessionBuilder */ private void initializeData(FullTextSessionBuilder fulltextSessionBuilder) { FullTextSession fullTextSession = fulltextSessionBuilder.openFullTextSession(); try { final Transaction transaction = fullTextSession.beginTransaction(); Catalog catalog = new Catalog(); catalog.setCatalogId( 1L ); catalog.setName( "parts" ); fullTextSession.persist( catalog ); for ( int i = 0; i < 5; i++ ) { Item item = new Item(); item.setName( "battery" ); fullTextSession.persist( item ); CatalogItem catalogItem = new CatalogItem(); catalogItem.setCatalog( catalog ); catalogItem.setItem( item ); fullTextSession.persist( catalogItem ); item.getCatalogItems().add( catalogItem ); fullTextSession.merge( item ); catalog.getCatalogItems().add( catalogItem ); fullTextSession.merge( catalog ); } transaction.commit(); } finally { fullTextSession.close(); } } /** * Update a non-indexed collection of an entity contained in a collection. No indexing work should be created. */ private void updateCatalogsCollection(FullTextSession fullTextSession, Catalog catalog) { final Transaction transaction = fullTextSession.beginTransaction(); Consumer consumer = new Consumer(); consumer.setName( "consumer" ); consumer.getCatalogs().add( catalog ); fullTextSession.persist( consumer ); catalog.getConsumers().add( consumer ); fullTextSession.merge( catalog ); transaction.commit(); } }