[hibernate-commits] Hibernate SVN: r15532 - in search/trunk/src: java/org/hibernate/search/impl and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Fri Nov 7 11:06:17 EST 2008


Author: hardy.ferentschik
Date: 2008-11-07 11:06:17 -0500 (Fri, 07 Nov 2008)
New Revision: 15532

Added:
   search/trunk/src/test/org/hibernate/search/test/id/Animal.java
   search/trunk/src/test/org/hibernate/search/test/id/ImplicitIdTest.java
Modified:
   search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
   search/trunk/src/java/org/hibernate/search/impl/InitContext.java
Log:
HSEARCH-104
Use @Id when no @DocumentId is specified.

Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java	2008-11-07 11:58:00 UTC (rev 15531)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java	2008-11-07 16:06:17 UTC (rev 15532)
@@ -3,6 +3,8 @@
 
 import java.io.Serializable;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
+import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -75,6 +77,11 @@
 	private final DirectoryProvider[] directoryProviders;
 	private final IndexShardingStrategy shardingStrategy;
 	private String idKeywordName;
+
+	/**
+	 * Flag indicating whether <code>@DocumentId</code> was explicitly specified.
+	 */
+	private boolean explicitDocumentId = false;
 	private XMember idGetter;
 	private Float idBoost;
 	public static final String CLASS_FIELDNAME = "_hibernate_class";
@@ -91,13 +98,8 @@
 	private boolean idProvided = false;
 	private EntityState entityState;
 
-
-	public boolean isRoot() {
-		return isRoot;
-	}
-
 	/**
-	 * used on an @Indexed entity
+	 * Constructor used on an @Indexed entity.
 	 */
 	public DocumentBuilder(XClass clazz, InitContext context, DirectoryProvider[] directoryProviders,
 						   IndexShardingStrategy shardingStrategy, ReflectionManager reflectionManager) {
@@ -135,7 +137,7 @@
 	}
 
 	/**
-	 * used on a non @Indexed entity
+	 * Constructor used on a non @Indexed entity.
 	 */
 	public DocumentBuilder(XClass clazz, InitContext context, ReflectionManager reflectionManager) {
 		this.entityState = EntityState.CONTAINED_IN_ONLY;
@@ -153,6 +155,10 @@
 		}
 	}
 
+	public boolean isRoot() {
+		return isRoot;
+	}
+
 	private ProvidedId findProvidedId(XClass clazz, ReflectionManager reflectionManager) {
 		ProvidedId id = null;
 		XClass currentClass = clazz;
@@ -214,7 +220,7 @@
 			if ( analyzer != null ) {
 				propertiesMetadata.analyzer = analyzer;
 			}
-			getAnalyzerDefs( currClass, context );
+			checkForAnalyzerDefs( currClass, context );
 			// Check for any ClassBridges annotation.
 			ClassBridges classBridgesAnn = currClass.getAnnotation( ClassBridges.class );
 			if ( classBridgesAnn != null ) {
@@ -265,7 +271,7 @@
 		}
 	}
 
-	private void getAnalyzerDefs(XAnnotatedElement annotatedElement, InitContext context) {
+	private void checkForAnalyzerDefs(XAnnotatedElement annotatedElement, InitContext context) {
 		AnalyzerDefs defs = annotatedElement.getAnnotation( AnalyzerDefs.class );
 		if ( defs != null ) {
 			for (AnalyzerDef def : defs.value()) {
@@ -287,62 +293,41 @@
 	private void initializeMember(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
 								  String prefix, Set<XClass> processedClasses, InitContext context) {
 
-		DocumentId documentIdAnn = member.getAnnotation( DocumentId.class );
-		if ( documentIdAnn != null ) {
-			if ( isRoot ) {
-				if ( idKeywordName != null ) {
-					throw new AssertionFailure( "Two document id assigned: "
-							+ idKeywordName + " and " + BinderHelper.getAttributeName( member, documentIdAnn.name() ) );
-				}
-				idKeywordName = prefix + BinderHelper.getAttributeName( member, documentIdAnn.name() );
-				FieldBridge fieldBridge = BridgeFactory.guessType( null, member, reflectionManager );
-				if ( fieldBridge instanceof TwoWayFieldBridge ) {
-					idBridge = (TwoWayFieldBridge) fieldBridge;
-				}
-				else {
-					throw new SearchException(
-							"Bridge for document id does not implement TwoWayFieldBridge: " + member.getName() );
-				}
-				idBoost = getBoost( member, null );
-				setAccessible( member );
-				idGetter = member;
-			}
-			else {
-				//component should index their document id
-				setAccessible( member );
-				propertiesMetadata.fieldGetters.add( member );
-				String fieldName = prefix + BinderHelper.getAttributeName( member, documentIdAnn.name() );
-				propertiesMetadata.fieldNames.add( fieldName );
-				propertiesMetadata.fieldStore.add( getStore( Store.YES ) );
-				propertiesMetadata.fieldIndex.add( getIndex( Index.UN_TOKENIZED ) );
-				propertiesMetadata.fieldTermVectors.add( getTermVector( TermVector.NO ) );
-				propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( null, member, reflectionManager ) );
-				propertiesMetadata.fieldBoosts.add( getBoost( member, null ) );
-				// property > entity analyzer (no field analyzer)
-				Analyzer analyzer = getAnalyzer( member, context );
-				if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
-				if ( analyzer == null ) throw new AssertionFailure( "Analizer should not be undefined" );
-				this.analyzer.addScopedAnalyzer( fieldName, analyzer );
-			}
-		}
-		{
-			org.hibernate.search.annotations.Field fieldAnn =
-					member.getAnnotation( org.hibernate.search.annotations.Field.class );
-			if ( fieldAnn != null ) {
+		checkDocumentId( member, propertiesMetadata, isRoot, prefix, context );
+		checkForField( member, propertiesMetadata, prefix, context );
+		checkForFields( member, propertiesMetadata, prefix, context );
+		checkForAnalyzerDefs( member, context );
+		checkForIndexedEmbedded( member, propertiesMetadata, prefix, processedClasses, context );
+		checkForConstraintIn( member, propertiesMetadata );
+	}
+
+	private void checkForFields(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, InitContext context) {
+		org.hibernate.search.annotations.Fields fieldsAnn =
+				member.getAnnotation( org.hibernate.search.annotations.Fields.class );
+		if ( fieldsAnn != null ) {
+			for (org.hibernate.search.annotations.Field fieldAnn : fieldsAnn.value()) {
 				bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
 			}
 		}
-		{
-			org.hibernate.search.annotations.Fields fieldsAnn =
-					member.getAnnotation( org.hibernate.search.annotations.Fields.class );
-			if ( fieldsAnn != null ) {
-				for (org.hibernate.search.annotations.Field fieldAnn : fieldsAnn.value()) {
-					bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
-				}
-			}
+	}
+
+	private void checkForField(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, InitContext context) {
+		org.hibernate.search.annotations.Field fieldAnn =
+				member.getAnnotation( org.hibernate.search.annotations.Field.class );
+		if ( fieldAnn != null ) {
+			bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
 		}
-		getAnalyzerDefs( member, context );
+	}
 
+	private void checkForConstraintIn(XProperty member, PropertiesMetadata propertiesMetadata) {
+		ContainedIn containedAnn = member.getAnnotation( ContainedIn.class );
+		if ( containedAnn != null ) {
+			setAccessible( member );
+			propertiesMetadata.containedInGetters.add( member );
+		}
+	}
+
+	private void checkForIndexedEmbedded(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, Set<XClass> processedClasses, InitContext context) {
 		IndexedEmbedded embeddedAnn = member.getAnnotation( IndexedEmbedded.class );
 		if ( embeddedAnn != null ) {
 			int oldMaxLevel = maxLevel;
@@ -411,14 +396,103 @@
 			level--;
 			maxLevel = oldMaxLevel; //set back the the old max level
 		}
+	}
 
-		ContainedIn containedAnn = member.getAnnotation( ContainedIn.class );
-		if ( containedAnn != null ) {
-			setAccessible( member );
-			propertiesMetadata.containedInGetters.add( member );
+	private void checkDocumentId(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, InitContext context) {
+		Annotation idAnnotation = getIdAnnotation( member, context );
+		if ( idAnnotation != null ) {
+			String attributeName = getIdAttributeName( member, idAnnotation );
+			if ( isRoot ) {
+				if ( idKeywordName != null && explicitDocumentId ) {
+					throw new AssertionFailure( "Two document id assigned: "
+							+ idKeywordName + " and " + attributeName );
+				}
+				idKeywordName = prefix + attributeName;
+				FieldBridge fieldBridge = BridgeFactory.guessType( null, member, reflectionManager );
+				if ( fieldBridge instanceof TwoWayFieldBridge ) {
+					idBridge = (TwoWayFieldBridge) fieldBridge;
+				}
+				else {
+					throw new SearchException(
+							"Bridge for document id does not implement TwoWayFieldBridge: " + member.getName() );
+				}
+				idBoost = getBoost( member, null );
+				setAccessible( member );
+				idGetter = member;
+			}
+			else {
+				//component should index their document id
+				setAccessible( member );
+				propertiesMetadata.fieldGetters.add( member );
+				String fieldName = prefix + attributeName;
+				propertiesMetadata.fieldNames.add( fieldName );
+				propertiesMetadata.fieldStore.add( getStore( Store.YES ) );
+				propertiesMetadata.fieldIndex.add( getIndex( Index.UN_TOKENIZED ) );
+				propertiesMetadata.fieldTermVectors.add( getTermVector( TermVector.NO ) );
+				propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( null, member, reflectionManager ) );
+				propertiesMetadata.fieldBoosts.add( getBoost( member, null ) );
+				// property > entity analyzer (no field analyzer)
+				Analyzer analyzer = getAnalyzer( member, context );
+				if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
+				if ( analyzer == null ) throw new AssertionFailure( "Analizer should not be undefined" );
+				this.analyzer.addScopedAnalyzer( fieldName, analyzer );
+			}
 		}
 	}
 
+	/**
+	 * Checks whether the specified property contains an annotation used as document id.
+	 * This can either be an explicit <code>@DocumentId</code> or if no <code>@DocumentId</code> is specified a
+	 * JPA <code>@Id</code> annotation. The check for the JPA annotation is indirectly to avoid a hard dependency
+	 * to Hibernate Annotations.
+	 *
+	 * @param member the property to check for the id annotation.
+	 * @return the annotation used as document id or <code>null</code> if id annotation is specified on the property.
+	 */
+	private Annotation getIdAnnotation(XProperty member, InitContext context) {
+		// check for explicit DocumentId
+		Annotation documentIdAnn = member.getAnnotation( DocumentId.class );
+		if ( documentIdAnn != null ) {
+			explicitDocumentId = true;
+			return documentIdAnn;
+		}
+
+		// check for JPA @Id
+		if ( !explicitDocumentId && context.isJpaPresent() ) {
+			Class idClass;
+			try {
+				idClass = org.hibernate.util.ReflectHelper.classForName( "javax.persistence.Id", InitContext.class );
+			} catch ( ClassNotFoundException e ) {
+				throw new SearchException( "Unable to load @Id.class even though it should be present ?!" );
+			}
+			documentIdAnn = member.getAnnotation( idClass );
+			if ( documentIdAnn != null )
+					log.debug( "Found JPA id and using it as document id" );
+		}
+		return documentIdAnn;
+	}
+
+	/**
+	 *  Determines the property name for the document id. It is either the name of the property itself or the
+	 *  value of the name attribute of the <code>idAnnotation</code>.
+	 *
+	 * @param member the property used as id property.
+	 * @param idAnnotation the id annotation
+	 * @return property name to be used as document id.
+	 */
+	private String getIdAttributeName(XProperty member, Annotation idAnnotation) {
+		String name = null;
+		try {
+			Method m = idAnnotation.getClass().getMethod( "name" );
+			name = (String) m.invoke( idAnnotation );
+		}
+		catch ( Exception e ) {
+			// ignore
+		}
+
+		return BinderHelper.getAttributeName( member, name );
+	}
+
 	private void bindClassAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann, InitContext context) {
 		//FIXME name should be prefixed
 		String fieldName = prefix + ann.name();

Modified: search/trunk/src/java/org/hibernate/search/impl/InitContext.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/InitContext.java	2008-11-07 11:58:00 UTC (rev 15531)
+++ search/trunk/src/java/org/hibernate/search/impl/InitContext.java	2008-11-07 16:06:17 UTC (rev 15532)
@@ -19,7 +19,12 @@
 import org.hibernate.util.ReflectHelper;
 
 /**
+ * Provides access to some default configuration settings (eg default <code>Analyzer</code> or default
+ * <code>Similarity</code>) and checks whether certain optional libraries are available.
+ *
+ *
  * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
  */
 public class InitContext {
 	private final Map<String, AnalyzerDef> analyzerDefs = new HashMap<String, AnalyzerDef>();
@@ -27,11 +32,13 @@
 	private final Analyzer defaultAnalyzer;
 	private final Similarity defaultSimilarity;
 	private final boolean solrPresent;
+	private final boolean jpaPresent;
 
 	public InitContext(SearchConfiguration cfg) {
 		defaultAnalyzer = initAnalyzer(cfg);
 		defaultSimilarity = initSimilarity(cfg);
 		solrPresent = isPresent( "org.apache.solr.analysis.TokenizerFactory" );
+		jpaPresent = isPresent( "javax.persistence.Id" );
 	}
 
 	public void addAnalyzerDef(AnalyzerDef ann) {
@@ -68,8 +75,6 @@
 				analyzerClass = ReflectHelper.classForName(analyzerClassName);
 			} catch (Exception e) {
 				return buildLazyAnalyzer( analyzerClassName );
-//				throw new SearchException("Lucene analyzer class '" + analyzerClassName + "' defined in property '"
-//						+ Environment.ANALYZER_CLASS + "' could not be found.", e);
 			}
 		} else {
 			analyzerClass = StandardAnalyzer.class;
@@ -88,7 +93,10 @@
 	}
 
 	/**
-	 * Initializes the Lucene similarity to use
+	 * Initializes the Lucene similarity to use.
+	 *
+	 * @param cfg the search configuration.
+	 * @return returns the default similarity class.
 	 */
 	private Similarity initSimilarity(SearchConfiguration cfg) {
 		Class similarityClass;
@@ -165,8 +173,8 @@
 		if ( ! solrPresent ) {
 			throw new SearchException( "Use of @AnalyzerDef while Solr is not present in the classpath. Add apache-solr-analyzer.jar" );
 		}
-		//SolrAnalyzerBuilder references Solr classes.
-		//InitContext should not (directly or indirectly) load a Solr class to avoid hard dependency
+		// SolrAnalyzerBuilder references Solr classes.
+		// InitContext should not (directly or indirectly) load a Solr class to avoid hard dependency
 		// unless necessary
 		// the curent mecanism (check sor class presence and call SolrAnalyzerBuilder if needed
 		// seems to be sufficient on Apple VM (derived from Sun's
@@ -174,6 +182,10 @@
 		return SolrAnalyzerBuilder.buildAnalyzer( analyzerDef );
 	}
 
+	public boolean isJpaPresent() {
+		return jpaPresent;
+	}
+
 	private boolean isPresent(String classname) {
 		try {
 			ReflectHelper.classForName( classname, InitContext.class );


Property changes on: search/trunk/src/java/org/hibernate/search/impl/InitContext.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: search/trunk/src/test/org/hibernate/search/test/id/Animal.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/id/Animal.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/id/Animal.java	2008-11-07 16:06:17 UTC (rev 15532)
@@ -0,0 +1,56 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.hibernate.search.test.id;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Indexed;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+ at Indexed(index = "Animal")
+public class Animal {
+	@Id @GeneratedValue
+	private Integer id;
+
+	@Field(index = Index.TOKENIZED)
+	private String name;
+
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+}

Copied: search/trunk/src/test/org/hibernate/search/test/id/ImplicitIdTest.java (from rev 15529, search/trunk/src/test/org/hibernate/search/test/id/EmbeddedIdTest.java)
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/id/ImplicitIdTest.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/id/ImplicitIdTest.java	2008-11-07 16:06:17 UTC (rev 15532)
@@ -0,0 +1,48 @@
+// $Id$
+package org.hibernate.search.test.id;
+
+import java.util.List;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.TermQuery;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.search.Search;
+import org.hibernate.search.test.SearchTestCase;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class ImplicitIdTest extends SearchTestCase {
+
+	/**
+	 * Tests that @DocumentId is optional. See HSEARCH-104.
+	 *
+	 * @throws Exception in case the test fails.
+	 */
+	public void testImplicitDocumentId() throws Exception {
+		Animal dog = new Animal();
+		dog.setName( "Dog" );
+
+		Session s = openSession();
+		Transaction tx = s.beginTransaction();
+		s.save( dog );
+		tx.commit();
+		s.clear();
+
+		tx = s.beginTransaction();
+		List results = Search.getFullTextSession( s ).createFullTextQuery(
+				new TermQuery( new Term( "name", "dog" ) )
+		).list();
+		assertEquals( 1, results.size() );
+		tx.commit();
+		s.close();
+	}
+
+	protected Class[] getMappings() {
+		return new Class[] {
+				Animal.class
+		};
+	}
+}
\ No newline at end of file


Property changes on: search/trunk/src/test/org/hibernate/search/test/id/ImplicitIdTest.java
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:mergeinfo
   + 




More information about the hibernate-commits mailing list