Hibernate SVN: r15608 - in branches/Branch_3_2/HibernateExt/tools/src: java/org/hibernate/tool and 3 other directories.
by hibernate-commits@lists.jboss.org
Author: max.andersen(a)jboss.com
Date: 2008-11-24 11:27:32 -0500 (Mon, 24 Nov 2008)
New Revision: 15608
Modified:
branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java
branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java
branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/Version.java
branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JDBCConfigurationTask.java
branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/OneToOneTest.java
branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OneToOneTest.java
Log:
HBX-524 one-to-one
* settings added to have eclipse support detect-one-to-one
* ant support detect-one-to-one
* updated tests to trigger found JPA gen bug
Modified: branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java 2008-11-24 15:46:29 UTC (rev 15607)
+++ branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java 2008-11-24 16:27:32 UTC (rev 15608)
@@ -231,9 +231,10 @@
public boolean isOneToOne(ForeignKey foreignKey) {
if(settings.getDetectOneToOne()) {
+ // add support for non-PK associations
List fkColumns = foreignKey.getColumns();
List pkForeignTableColumns = null;
-
+
if (foreignKey.getTable().hasPrimaryKey())
pkForeignTableColumns = foreignKey.getTable().getPrimaryKey().getColumns();
Modified: branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java 2008-11-24 15:46:29 UTC (rev 15607)
+++ branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java 2008-11-24 16:27:32 UTC (rev 15608)
@@ -74,6 +74,11 @@
return detectManyToMany;
}
+ public ReverseEngineeringSettings setDetectOneToOne(boolean b) {
+ this.detectOneToOne = b;
+ return this;
+ }
+
public boolean getDetectOneToOne() {
return detectOneToOne;
}
Modified: branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/Version.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/Version.java 2008-11-24 15:46:29 UTC (rev 15607)
+++ branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/Version.java 2008-11-24 16:27:32 UTC (rev 15608)
@@ -5,7 +5,7 @@
final public class Version {
- public static final String VERSION = "3.2.2.GA";
+ public static final String VERSION = "3.2.4.CR1";
private static final Version instance = new Version();
Modified: branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JDBCConfigurationTask.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JDBCConfigurationTask.java 2008-11-24 15:46:29 UTC (rev 15607)
+++ branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JDBCConfigurationTask.java 2008-11-24 16:27:32 UTC (rev 15608)
@@ -24,13 +24,14 @@
* @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
*/
public class JDBCConfigurationTask extends ConfigurationTask {
- //not exposed here.
+ //not expfosed here.
private boolean preferBasicCompositeIds = true;
private String reverseEngineeringStrategyClass;
private String packageName;
private Path revengFiles;
+ private boolean detectOneToOne = true;
private boolean detectManyToMany = true;
private boolean detectOptimisticLock = true;
@@ -71,6 +72,7 @@
ReverseEngineeringSettings qqsettings =
new ReverseEngineeringSettings(strategy).setDefaultPackageName(packageName)
.setDetectManyToMany( detectManyToMany )
+ .setDetectOneToOne( detectOneToOne )
.setDetectOptimisticLock( detectOptimisticLock );
defaultStrategy.setSettings(qqsettings);
@@ -98,6 +100,10 @@
preferBasicCompositeIds = b;
}
+ public void setDetectOneToOne(boolean b) {
+ detectOneToOne = b;
+ }
+
public void setDetectManyToMany(boolean b) {
detectManyToMany = b;
}
Modified: branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/OneToOneTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/OneToOneTest.java 2008-11-24 15:46:29 UTC (rev 15607)
+++ branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/OneToOneTest.java 2008-11-24 16:27:32 UTC (rev 15608)
@@ -20,11 +20,15 @@
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
import org.hibernate.tool.NonReflectiveTestCase;
+import org.hibernate.tool.hbm2ddl.SchemaValidator;
import org.hibernate.tool.hbm2x.Exporter;
import org.hibernate.tool.hbm2x.HibernateMappingExporter;
import org.hibernate.util.DTDEntityResolver;
+
+
public class OneToOneTest extends NonReflectiveTestCase {
private Exporter hbmexporter;
@@ -60,6 +64,7 @@
cfg.addFile(new File(getOutputDir(), getBaseForMappings() + "Address.hbm.xml"));
cfg.buildMappings();
+
}
Modified: branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OneToOneTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OneToOneTest.java 2008-11-24 15:46:29 UTC (rev 15607)
+++ branches/Branch_3_2/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OneToOneTest.java 2008-11-24 16:27:32 UTC (rev 15608)
@@ -24,6 +24,7 @@
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.tool.JDBCMetaDataBinderTestCase;
+import org.hibernate.tool.hbm2ddl.SchemaValidator;
import org.hibernate.tool.hbm2x.HibernateMappingExporter;
import org.hibernate.tool.hbm2x.POJOExporter;
import org.hibernate.tool.test.TestHelper;
@@ -171,9 +172,10 @@
public void testBuildMappings() {
localCfg.buildMappings();
+
}
- public void testGenerateMappingAndReadable() {
+ public void testGenerateMappingAndReadable() throws MalformedURLException {
cfg.buildMappings();
@@ -186,7 +188,27 @@
assertFileAndExists( new File(getOutputDir(), "MultiPerson.hbm.xml") );
assertEquals(4, getOutputDir().listFiles().length);
+
+ POJOExporter exporter = new POJOExporter(cfg, getOutputDir() );
+ exporter.setTemplatePath(new String[0]);
+ exporter.getProperties().setProperty("ejb3", "false");
+ exporter.getProperties().setProperty("jdk5", "false");
+ exporter.start();
+ ArrayList list = new ArrayList();
+ List jars = new ArrayList();
+ addAnnotationJars(jars);
+ TestHelper.compile(
+ getOutputDir(), getOutputDir(), TestHelper.visitAllFiles( getOutputDir(), list ), "1.5",
+ TestHelper.buildClasspath( jars )
+ );
+
+ URL[] urls = new URL[] { getOutputDir().toURL() };
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ URLClassLoader ucl = new URLClassLoader(urls, oldLoader );
+ try {
+ Thread.currentThread().setContextClassLoader(ucl);
+
Configuration configuration = new Configuration()
.addFile( new File(getOutputDir(), "Person.hbm.xml") )
.addFile( new File(getOutputDir(), "AddressPerson.hbm.xml") )
@@ -194,7 +216,11 @@
.addFile( new File(getOutputDir(), "MultiPerson.hbm.xml"));
configuration.buildMappings();
-
+
+ new SchemaValidator(configuration).validate();
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldLoader);
+ }
}
public void testGenerateAnnotatedClassesAndReadable() throws MappingException, ClassNotFoundException, MalformedURLException {
@@ -222,13 +248,15 @@
TestHelper.buildClasspath( jars )
);
URL[] urls = new URL[] { getOutputDir().toURL() };
- URLClassLoader ucl = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader() );
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ URLClassLoader ucl = new URLClassLoader(urls, oldLoader );
Class personClass = ucl.loadClass("Person");
Class multiPersonClass = ucl.loadClass("MultiPerson");
Class addressMultiPerson = ucl.loadClass("AddressMultiPerson");
Class addressMultiPersonId = ucl.loadClass("AddressMultiPersonId");
Class addressPerson = ucl.loadClass("AddressPerson");
Class multiPersonIdClass = ucl.loadClass("MultiPersonId");
+ try {
Thread.currentThread().setContextClassLoader(ucl);
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.addAnnotatedClass(personClass)
@@ -239,6 +267,10 @@
.addAnnotatedClass(multiPersonIdClass);
configuration.buildMappings();
+ new SchemaValidator(configuration).validate();
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldLoader);
+ }
}
16 years
Hibernate SVN: r15607 - search/trunk/src/java/org/hibernate/search/engine.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2008-11-24 10:46:29 -0500 (Mon, 24 Nov 2008)
New Revision: 15607
Modified:
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
Log:
HSEARCH-285 removed deprecation on DocumentBuilder
Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-11-24 11:39:52 UTC (rev 15606)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-11-24 15:46:29 UTC (rev 15607)
@@ -6,7 +6,6 @@
/**
* Interface created to keep backwards compatibility.
*
- * @deprecated As of release 3.1.0, replaced by {@link org.hibernate.search.ProjectionConstants}
* @author Hardy Ferentschik
*/
public interface DocumentBuilder {
@@ -14,8 +13,6 @@
/**
* Lucene document field name containing the fully qualified classname of the indexed class.
*
- * @deprecated As of release 3.1.0, replaced by {@link org.hibernate.search.ProjectionConstants#OBJECT_CLASS}
*/
- @Deprecated
String CLASS_FIELDNAME = ProjectionConstants.OBJECT_CLASS;
}
16 years
Hibernate SVN: r15606 - in search/trunk/src: java/org/hibernate/search/backend/impl/lucene/works and 4 other directories.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2008-11-24 06:39:52 -0500 (Mon, 24 Nov 2008)
New Revision: 15606
Added:
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
Modified:
search/trunk/src/java/org/hibernate/search/ProjectionConstants.java
search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java
search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java
search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
search/trunk/src/test/org/hibernate/search/test/query/ProjectionQueryTest.java
Log:
HSEARCH-285 & HSEARCH-296
Introduced a DocumetBuilder interface containing the CLASS_FIELDNAME constant. Deprecated the interface.
Also introduced ProjectionConstant.OBJECT_CLASS in order to project indexed class type
Modified: search/trunk/src/java/org/hibernate/search/ProjectionConstants.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/ProjectionConstants.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/java/org/hibernate/search/ProjectionConstants.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -2,13 +2,13 @@
package org.hibernate.search;
/**
- * Define Projection constants
+ * Defined projection constants.
*
* @author Emmanuel Bernard
*/
public interface ProjectionConstants {
/**
- * Represtnts the Hibernate Entity returned in a search.
+ * Represents the Hibernate entity returned in a search.
*/
public String THIS = "__HSearch_This";
@@ -53,7 +53,8 @@
public String EXPLANATION = "__HSearch_Explanation";
/**
- * Object class
+ * Represents the Hibernate entity class returned in a search. In contrast to the other constants this constant
+ * represents an actual field value of the underlying Lucene document and hence can directly be used in queries.
*/
- //TODO OBJECT CLASS
+ public String OBJECT_CLASS = "_hibernate_class";
}
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -16,6 +16,7 @@
import org.hibernate.search.backend.Workspace;
import org.hibernate.search.backend.impl.lucene.IndexInteractionType;
import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
+import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.util.LoggerFactory;
/**
@@ -51,7 +52,7 @@
TermQuery idQueryTerm = new TermQuery( builder.getTerm( work.getId() ) );
entityDeletionQuery.add( idQueryTerm, BooleanClause.Occur.MUST );
- Term classNameQueryTerm = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, entityType.getName() );
+ Term classNameQueryTerm = new Term( DocumentBuilder.CLASS_FIELDNAME, entityType.getName() );
TermQuery classNameQuery = new TermQuery( classNameQueryTerm );
entityDeletionQuery.add( classNameQuery, BooleanClause.Occur.MUST );
@@ -88,7 +89,7 @@
String entityName = entityType.getName();
while ( termDocs.next() ) {
int docIndex = termDocs.doc();
- if ( entityName.equals( reader.document( docIndex ).get( DocumentBuilderIndexedEntity.CLASS_FIELDNAME ) ) ) {
+ if ( entityName.equals( reader.document( docIndex ).get( DocumentBuilder.CLASS_FIELDNAME ) ) ) {
//remove only the one of the right class
//loop all to remove all the matches (defensive code)
reader.deleteDocument( docIndex );
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -8,7 +8,7 @@
import org.hibernate.search.SearchException;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.impl.lucene.IndexInteractionType;
-import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
+import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.util.LoggerFactory;
/**
@@ -34,7 +34,7 @@
public void performWork(LuceneWork work, IndexWriter writer) {
log.trace( "purgeAll Lucene index using IndexWriter for type: {}", work.getEntityClass() );
try {
- Term term = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, work.getEntityClass().getName() );
+ Term term = new Term( DocumentBuilder.CLASS_FIELDNAME, work.getEntityClass().getName() );
writer.deleteDocuments( term );
}
catch (Exception e) {
@@ -45,7 +45,7 @@
public void performWork(LuceneWork work, IndexReader reader) {
log.trace( "purgeAll Lucene index using IndexReader for type: {}", work.getEntityClass() );
try {
- Term term = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, work.getEntityClass().getName() );
+ Term term = new Term( DocumentBuilder.CLASS_FIELDNAME, work.getEntityClass().getName() );
reader.deleteDocuments( term );
}
catch (Exception e) {
Added: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -0,0 +1,21 @@
+// $Id$
+package org.hibernate.search.engine;
+
+import org.hibernate.search.ProjectionConstants;
+
+/**
+ * Interface created to keep backwards compatibility.
+ *
+ * @deprecated As of release 3.1.0, replaced by {@link org.hibernate.search.ProjectionConstants}
+ * @author Hardy Ferentschik
+ */
+public interface DocumentBuilder {
+
+ /**
+ * Lucene document field name containing the fully qualified classname of the indexed class.
+ *
+ * @deprecated As of release 3.1.0, replaced by {@link org.hibernate.search.ProjectionConstants#OBJECT_CLASS}
+ */
+ @Deprecated
+ String CLASS_FIELDNAME = ProjectionConstants.OBJECT_CLASS;
+}
Property changes on: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -63,11 +63,9 @@
* @author Richard Hallier
* @author Hardy Ferentschik
*/
-public class DocumentBuilderContainedEntity<T> {
+public class DocumentBuilderContainedEntity<T> implements DocumentBuilder {
private static final Logger log = LoggerFactory.make();
- public static final String CLASS_FIELDNAME = "_hibernate_class";
-
protected final PropertiesMetadata metadata = new PropertiesMetadata();
protected final XClass beanClass;
protected String idKeywordName;
@@ -830,7 +828,7 @@
}
public static Class getDocumentClass(Document document) {
- String className = document.get( DocumentBuilderContainedEntity.CLASS_FIELDNAME );
+ String className = document.get( CLASS_FIELDNAME );
try {
return ReflectHelper.classForName( className );
}
Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -13,6 +13,7 @@
import org.apache.lucene.document.FieldSelector;
import org.hibernate.search.ProjectionConstants;
+import org.hibernate.search.SearchException;
import org.hibernate.search.query.QueryHits;
/**
@@ -49,10 +50,9 @@
}
}
-
// set up the field selector. CLASS_FIELDNAME and id fields are needed on top of any projected fields
Map<String, FieldSelectorResult> fields = new HashMap<String, FieldSelectorResult>( 1 + idFieldNames.size() + projectionSize );
- fields.put( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, FieldSelectorResult.LOAD );
+ fields.put( DocumentBuilder.CLASS_FIELDNAME, FieldSelectorResult.LOAD );
for ( String idFieldName : idFieldNames ) {
fields.put( idFieldName, FieldSelectorResult.LOAD );
}
@@ -69,7 +69,9 @@
Serializable id = DocumentBuilderIndexedEntity.getDocumentId( searchFactoryImplementor, clazz, document );
Object[] projected = null;
if ( projection != null && projection.length > 0 ) {
- projected = DocumentBuilderIndexedEntity.getDocumentFields( searchFactoryImplementor, clazz, document, projection );
+ projected = DocumentBuilderIndexedEntity.getDocumentFields(
+ searchFactoryImplementor, clazz, document, projection
+ );
}
return new EntityInfo( clazz, id, projected );
}
@@ -82,10 +84,11 @@
else {
doc = queryHits.doc( index );
}
- //TODO if we are only looking for score (unlikely), avoid accessing doc (lazy load)
+
EntityInfo entityInfo = extract( doc );
Object[] eip = entityInfo.projection;
+ // TODO - if we are only looking for score (unlikely), avoid accessing doc (lazy load)
if ( eip != null && eip.length > 0 ) {
for ( int x = 0; x < projection.length; x++ ) {
if ( ProjectionConstants.SCORE.equals( projection[x] ) ) {
@@ -106,6 +109,9 @@
else if ( ProjectionConstants.EXPLANATION.equals( projection[x] ) ) {
eip[x] = queryHits.explain( index );
}
+ else if ( ProjectionConstants.OBJECT_CLASS.equals( projection[x] ) ) {
+ eip[x] = entityInfo.clazz;
+ }
else if ( ProjectionConstants.THIS.equals( projection[x] ) ) {
//THIS could be projected more than once
//THIS loading delayed to the Loader phase
Modified: search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -50,6 +50,7 @@
import org.hibernate.search.engine.ProjectionLoader;
import org.hibernate.search.engine.QueryLoader;
import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.filter.ChainedFilter;
import org.hibernate.search.filter.FilterKey;
import org.hibernate.search.filter.StandardFilterKey;
@@ -550,7 +551,7 @@
//annihilate the scoring impact of DocumentBuilderIndexedEntity.CLASS_FIELDNAME
classFilter.setBoost( 0 );
for ( Class clazz : classesAndSubclasses ) {
- Term t = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, clazz.getName() );
+ Term t = new Term( DocumentBuilder.CLASS_FIELDNAME, clazz.getName() );
TermQuery termQuery = new TermQuery( t );
classFilter.add( termQuery, BooleanClause.Occur.SHOULD );
}
Modified: search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -23,7 +23,7 @@
import org.hibernate.search.Environment;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
-import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
+import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.backend.AddLuceneWork;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
@@ -54,7 +54,7 @@
s.close();
//create the work queue to send
Document doc = new Document();
- Field field = new Field( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, ts.getClass().getName(), Field.Store.YES, Field.Index.NOT_ANALYZED );
+ Field field = new Field( DocumentBuilder.CLASS_FIELDNAME, ts.getClass().getName(), Field.Store.YES, Field.Index.NOT_ANALYZED );
doc.add( field );
field = new Field("id", "1", Field.Store.YES, Field.Index.NOT_ANALYZED );
doc.add( field );
Modified: search/trunk/src/test/org/hibernate/search/test/query/ProjectionQueryTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/query/ProjectionQueryTest.java 2008-11-23 10:48:59 UTC (rev 15605)
+++ search/trunk/src/test/org/hibernate/search/test/query/ProjectionQueryTest.java 2008-11-24 11:39:52 UTC (rev 15606)
@@ -23,11 +23,41 @@
import org.hibernate.search.test.SearchTestCase;
/**
+ * Tests several aspects of projection queries.
+ *
* @author Emmanuel Bernard
* @author John Griffin
+ * @author Hardy Ferentschik
*/
public class ProjectionQueryTest extends SearchTestCase {
+ /**
+ * HSEARCH-296
+ *
+ * @throws Exception in case the test fails.
+ */
+ public void testClassProjection() throws Exception {
+ FullTextSession s = Search.getFullTextSession( openSession() );
+ prepEmployeeIndex( s );
+
+ s.clear();
+ Transaction tx = s.beginTransaction();
+ QueryParser parser = new QueryParser( "dept", new StandardAnalyzer() );
+ Query query = parser.parse( "dept:ITech" );
+ org.hibernate.search.FullTextQuery hibQuery = s.createFullTextQuery( query, Employee.class );
+ hibQuery.setProjection( FullTextQuery.OBJECT_CLASS );
+
+ List result = hibQuery.list();
+ assertNotNull( result );
+
+ Object[] projection = ( Object[] ) result.get( 0 );
+ assertNotNull( projection );
+ assertEquals( "Wrong projected class", Employee.class, projection[0] );
+
+ tx.commit();
+ s.close();
+ }
+
public void testLuceneObjectsProjectionWithScroll() throws Exception {
FullTextSession s = Search.getFullTextSession( openSession() );
prepEmployeeIndex( s );
@@ -108,7 +138,7 @@
hibQuery.setProjection( "id", "lastname", "dept", FullTextQuery.THIS, FullTextQuery.SCORE, FullTextQuery.ID );
hibQuery.setResultTransformer( new ProjectionToDelimStringResultTransformer() );
- @SuppressWarnings( "unchecked" )
+ @SuppressWarnings("unchecked")
List<String> result = hibQuery.list();
assertTrue( "incorrect transformation", result.get( 0 ).startsWith( "1000, Griffin, ITech" ) );
assertTrue( "incorrect transformation", result.get( 1 ).startsWith( "1002, Jimenez, ITech" ) );
@@ -436,7 +466,6 @@
s.close();
}
-
protected Class[] getMappings() {
return new Class[] {
Book.class,
16 years
Hibernate SVN: r15605 - in search/trunk/src: java/org/hibernate/search/backend/impl and 10 other directories.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2008-11-23 05:48:59 -0500 (Sun, 23 Nov 2008)
New Revision: 15605
Added:
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java
search/trunk/src/java/org/hibernate/search/util/ReflectionHelper.java
Removed:
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
search/trunk/src/java/org/hibernate/search/util/BinderHelper.java
Modified:
search/trunk/src/java/org/hibernate/search/backend/Workspace.java
search/trunk/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java
search/trunk/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java
search/trunk/src/java/org/hibernate/search/backend/impl/lucene/LuceneBackendQueueProcessor.java
search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/AddWorkDelegate.java
search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteExtWorkDelegate.java
search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java
search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java
search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java
search/trunk/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java
search/trunk/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java
search/trunk/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java
search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java
search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java
search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java
search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
search/trunk/src/test/org/hibernate/search/test/worker/duplication/WorkDuplicationTest.java
Log:
HSEARCH-285
Split DocumentBuilder into DocumentBuilderConstainedEntity and DocumentBuilderIndexedEntity.
Modified: search/trunk/src/java/org/hibernate/search/backend/Workspace.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/Workspace.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/Workspace.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -16,7 +16,7 @@
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.search.SearchException;
import org.hibernate.search.SearchFactory;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.store.optimization.OptimizerStrategy;
@@ -77,8 +77,8 @@
this.lock = searchFactoryImplementor.getDirectoryProviderLock( provider );
}
- public <T> DocumentBuilder<T> getDocumentBuilder(Class<T> entity) {
- return searchFactoryImplementor.getDocumentBuilder( entity );
+ public <T> DocumentBuilderIndexedEntity<T> getDocumentBuilder(Class<T> entity) {
+ return searchFactoryImplementor.getDocumentBuilderIndexedEntity( entity );
}
/**
@@ -206,8 +206,6 @@
}
}
-
-
/**
* Closes a previously opened IndexWriter.
* @throws SearchException on IOException during Lucene close operation.
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -25,13 +25,14 @@
import org.hibernate.search.backend.configuration.ConfigurationParseHelper;
import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
import org.hibernate.search.backend.impl.lucene.LuceneBackendQueueProcessorFactory;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.engine.DocumentBuilderContainedEntity;
import org.hibernate.search.util.LoggerFactory;
/**
- * Batch work until #performWorks is called.
- * The work is then executed synchronously or asynchronously
+ * Batch work until {@link #performWorks} is called.
+ * The work is then executed synchronously or asynchronously.
*
* @author Emmanuel Bernard
*/
@@ -148,17 +149,21 @@
Class<T> entityClass = work.getEntityClass() != null ?
work.getEntityClass() :
Hibernate.getClass( work.getEntity() );
- DocumentBuilder<T> builder = searchFactoryImplementor.getDocumentBuilder( entityClass );
- if ( builder == null ) {
- //might be a entity contained in
- builder = searchFactoryImplementor.getContainedInOnlyBuilder( entityClass );
- }
- if ( builder == null ) {
+ DocumentBuilderIndexedEntity<T> entityBuilder = searchFactoryImplementor.getDocumentBuilderIndexedEntity( entityClass );
+ if ( entityBuilder != null ) {
+ entityBuilder.addWorkToQueue(
+ entityClass, work.getEntity(), work.getId(), work.getType(), luceneQueue, searchFactoryImplementor
+ );
return;
}
- builder.addWorkToQueue(
- entityClass, work.getEntity(), work.getId(), work.getType(), luceneQueue, searchFactoryImplementor
- );
+
+ //might be a entity contained in
+ DocumentBuilderContainedEntity<T> containedInBuilder = searchFactoryImplementor.getDocumentBuilderContainedEntity( entityClass );
+ if ( containedInBuilder != null ) {
+ containedInBuilder.addWorkToQueue(
+ entityClass, work.getEntity(), work.getId(), work.getType(), luceneQueue, searchFactoryImplementor
+ );
+ }
}
//TODO implements parallel batchWorkers (one per Directory)
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -3,6 +3,8 @@
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;
@@ -22,16 +24,18 @@
*/
public class TransactionalWorker implements Worker {
//not a synchronized map since for a given transaction, we have not concurrent access
- protected final WeakIdentityHashMap synchronizationPerTransaction = new WeakIdentityHashMap();
+ 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)
+ PostTransactionWorkQueueSynchronization txSync = ( PostTransactionWorkQueueSynchronization )
synchronizationPerTransaction.get( transaction );
if ( txSync == null || txSync.isConsumed() ) {
- txSync = new PostTransactionWorkQueueSynchronization( queueingProcessor, synchronizationPerTransaction );
+ txSync = new PostTransactionWorkQueueSynchronization(
+ queueingProcessor, synchronizationPerTransaction
+ );
transactionContext.registerSynchronization( txSync );
synchronizationPerTransaction.put( transaction, txSync );
}
@@ -53,11 +57,10 @@
queueingProcessor.close();
}
-
public void flushWorks(TransactionContext transactionContext) {
if ( transactionContext.isTransactionInProgress() ) {
Object transaction = transactionContext.getTransactionIdentifier();
- PostTransactionWorkQueueSynchronization txSync = (PostTransactionWorkQueueSynchronization)
+ PostTransactionWorkQueueSynchronization txSync = ( PostTransactionWorkQueueSynchronization )
synchronizationPerTransaction.get( transaction );
if ( txSync != null && !txSync.isConsumed() ) {
txSync.flushWorks();
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/lucene/LuceneBackendQueueProcessor.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/lucene/LuceneBackendQueueProcessor.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/lucene/LuceneBackendQueueProcessor.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -6,7 +6,7 @@
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.impl.lucene.works.LuceneWorkVisitor;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.store.IndexShardingStrategy;
@@ -39,7 +39,7 @@
// divide tasks in parts, adding to QueueProcessors by affected Directory.
for ( LuceneWork work : queue ) {
final Class<?> entityType = work.getEntityClass();
- DocumentBuilder<?> documentBuilder = searchFactoryImplementor.getDocumentBuilder( entityType );
+ DocumentBuilderIndexedEntity<?> documentBuilder = searchFactoryImplementor.getDocumentBuilderIndexedEntity( entityType );
IndexShardingStrategy shardingStrategy = documentBuilder.getDirectoryProviderSelectionStrategy();
work.getWorkDelegate( providerSelectionVisitor ).addAsPayLoadsToQueue( work, shardingStrategy, processors );
}
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/AddWorkDelegate.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/AddWorkDelegate.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/AddWorkDelegate.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -12,7 +12,7 @@
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.Workspace;
import org.hibernate.search.backend.impl.lucene.IndexInteractionType;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.util.LoggerFactory;
/**
@@ -40,7 +40,7 @@
}
public void performWork(LuceneWork work, IndexWriter writer) {
- DocumentBuilder documentBuilder = workspace.getDocumentBuilder( work.getEntityClass() );
+ DocumentBuilderIndexedEntity documentBuilder = workspace.getDocumentBuilder( work.getEntityClass() );
Analyzer analyzer = documentBuilder.getAnalyzer();
Similarity similarity = documentBuilder.getSimilarity();
if ( log.isTraceEnabled() ) {
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteExtWorkDelegate.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteExtWorkDelegate.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteExtWorkDelegate.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -10,7 +10,7 @@
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.Workspace;
import org.hibernate.search.backend.impl.lucene.IndexInteractionType;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.util.LoggerFactory;
import org.slf4j.Logger;
@@ -26,7 +26,7 @@
public class DeleteExtWorkDelegate extends DeleteWorkDelegate {
private final Class managedType;
- private final DocumentBuilder builder;
+ private final DocumentBuilderIndexedEntity builder;
private final Logger log = LoggerFactory.make();
DeleteExtWorkDelegate(Workspace workspace) {
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/DeleteWorkDelegate.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -15,7 +15,7 @@
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.Workspace;
import org.hibernate.search.backend.impl.lucene.IndexInteractionType;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.util.LoggerFactory;
/**
@@ -44,14 +44,14 @@
public void performWork(LuceneWork work, IndexWriter writer) {
final Class<?> entityType = work.getEntityClass();
log.trace( "Removing {}#{} by query.", entityType, work.getId() );
- DocumentBuilder<?> builder = workspace.getDocumentBuilder( entityType );
+ DocumentBuilderIndexedEntity<?> builder = workspace.getDocumentBuilder( entityType );
BooleanQuery entityDeletionQuery = new BooleanQuery();
TermQuery idQueryTerm = new TermQuery( builder.getTerm( work.getId() ) );
entityDeletionQuery.add( idQueryTerm, BooleanClause.Occur.MUST );
- Term classNameQueryTerm = new Term( DocumentBuilder.CLASS_FIELDNAME, entityType.getName() );
+ Term classNameQueryTerm = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, entityType.getName() );
TermQuery classNameQuery = new TermQuery( classNameQueryTerm );
entityDeletionQuery.add( classNameQuery, BooleanClause.Occur.MUST );
@@ -78,7 +78,7 @@
*/
final Class<?> entityType = work.getEntityClass();
log.trace( "Removing {}#{} from Lucene index.", entityType, work.getId() );
- DocumentBuilder<?> builder = workspace.getDocumentBuilder( entityType );
+ DocumentBuilderIndexedEntity<?> builder = workspace.getDocumentBuilder( entityType );
Term term = builder.getTerm( work.getId() );
TermDocs termDocs = null;
try {
@@ -88,7 +88,7 @@
String entityName = entityType.getName();
while ( termDocs.next() ) {
int docIndex = termDocs.doc();
- if ( entityName.equals( reader.document( docIndex ).get( DocumentBuilder.CLASS_FIELDNAME ) ) ) {
+ if ( entityName.equals( reader.document( docIndex ).get( DocumentBuilderIndexedEntity.CLASS_FIELDNAME ) ) ) {
//remove only the one of the right class
//loop all to remove all the matches (defensive code)
reader.deleteDocument( docIndex );
Modified: search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/backend/impl/lucene/works/PurgeAllWorkDelegate.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -8,7 +8,7 @@
import org.hibernate.search.SearchException;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.impl.lucene.IndexInteractionType;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.util.LoggerFactory;
/**
@@ -34,7 +34,7 @@
public void performWork(LuceneWork work, IndexWriter writer) {
log.trace( "purgeAll Lucene index using IndexWriter for type: {}", work.getEntityClass() );
try {
- Term term = new Term( DocumentBuilder.CLASS_FIELDNAME, work.getEntityClass().getName() );
+ Term term = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, work.getEntityClass().getName() );
writer.deleteDocuments( term );
}
catch (Exception e) {
@@ -45,7 +45,7 @@
public void performWork(LuceneWork work, IndexReader reader) {
log.trace( "purgeAll Lucene index using IndexReader for type: {}", work.getEntityClass() );
try {
- Term term = new Term( DocumentBuilder.CLASS_FIELDNAME, work.getEntityClass().getName() );
+ Term term = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, work.getEntityClass().getName() );
reader.deleteDocuments( term );
}
catch (Exception e) {
Deleted: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -1,1062 +0,0 @@
-//$Id$
-package org.hibernate.search.engine;
-
-import java.io.Serializable;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.Similarity;
-import org.slf4j.Logger;
-
-import org.hibernate.Hibernate;
-import org.hibernate.annotations.common.AssertionFailure;
-import org.hibernate.annotations.common.reflection.ReflectionManager;
-import org.hibernate.annotations.common.reflection.XAnnotatedElement;
-import org.hibernate.annotations.common.reflection.XClass;
-import org.hibernate.annotations.common.reflection.XMember;
-import org.hibernate.annotations.common.reflection.XProperty;
-import org.hibernate.annotations.common.util.ReflectHelper;
-import org.hibernate.annotations.common.util.StringHelper;
-import org.hibernate.proxy.HibernateProxy;
-import org.hibernate.search.SearchException;
-import org.hibernate.search.annotations.AnalyzerDef;
-import org.hibernate.search.annotations.AnalyzerDefs;
-import org.hibernate.search.annotations.Boost;
-import org.hibernate.search.annotations.ClassBridge;
-import org.hibernate.search.annotations.ClassBridges;
-import org.hibernate.search.annotations.ContainedIn;
-import org.hibernate.search.annotations.DocumentId;
-import org.hibernate.search.annotations.Index;
-import org.hibernate.search.annotations.IndexedEmbedded;
-import org.hibernate.search.annotations.ProvidedId;
-import org.hibernate.search.annotations.Store;
-import org.hibernate.search.annotations.TermVector;
-import org.hibernate.search.backend.AddLuceneWork;
-import org.hibernate.search.backend.DeleteLuceneWork;
-import org.hibernate.search.backend.LuceneWork;
-import org.hibernate.search.backend.PurgeAllLuceneWork;
-import org.hibernate.search.backend.WorkType;
-import org.hibernate.search.bridge.BridgeFactory;
-import org.hibernate.search.bridge.FieldBridge;
-import org.hibernate.search.bridge.LuceneOptions;
-import org.hibernate.search.bridge.TwoWayFieldBridge;
-import org.hibernate.search.bridge.TwoWayString2FieldBridgeAdaptor;
-import org.hibernate.search.bridge.TwoWayStringBridge;
-import org.hibernate.search.impl.InitContext;
-import org.hibernate.search.store.DirectoryProvider;
-import org.hibernate.search.store.IndexShardingStrategy;
-import org.hibernate.search.util.BinderHelper;
-import org.hibernate.search.util.LoggerFactory;
-import org.hibernate.search.util.ScopedAnalyzer;
-
-/**
- * Set up and provide a manager for indexed classes.
- *
- * @author Gavin King
- * @author Emmanuel Bernard
- * @author Sylvain Vieujot
- * @author Richard Hallier
- * @author Hardy Ferentschik
- */
-public class DocumentBuilder<T> {
- private static final Logger log = LoggerFactory.make();
-
- private final PropertiesMetadata rootPropertiesMetadata = new PropertiesMetadata();
- private final XClass beanClass;
- private final DirectoryProvider[] directoryProviders;
- private final IndexShardingStrategy shardingStrategy;
- private String idKeywordName;
-
- /**
- * Flag indicating whether <code>@DocumentId</code> was explicitly specified.
- */
- private boolean explicitDocumentId = false;
-
- /**
- * Flag indicating whether {@link org.apache.lucene.search.Searcher#doc(int, org.apache.lucene.document.FieldSelector)}
- * can be used in order to retrieve documents. This is only safe to do if we know that
- * all involved bridges are implementing <code>TwoWayStringBridge</code>. See HSEARCH-213.
- */
- private boolean allowFieldSelectionInProjection = false;
-
- private XMember idGetter;
- private Float idBoost;
- public static final String CLASS_FIELDNAME = "_hibernate_class";
- private TwoWayFieldBridge idBridge;
- private Set<Class<?>> mappedSubclasses = new HashSet<Class<?>>();
- private ReflectionManager reflectionManager; //available only during initializationa nd post-initialization
- private int level = 0;
- private int maxLevel = Integer.MAX_VALUE;
- private final ScopedAnalyzer analyzer = new ScopedAnalyzer();
- private Similarity similarity;
- private boolean isRoot;
- //if composite id, use of (a, b) in ((1,2), (3,4)) fails on most database
- private boolean safeFromTupleId;
- private boolean idProvided = false;
- private EntityState entityState;
-
- /**
- * Constructor used on an @Indexed entity.
- */
- public DocumentBuilder(XClass clazz, InitContext context, DirectoryProvider[] directoryProviders,
- IndexShardingStrategy shardingStrategy, ReflectionManager reflectionManager) {
- this.entityState = EntityState.INDEXED;
- this.beanClass = clazz;
- this.directoryProviders = directoryProviders;
- this.shardingStrategy = shardingStrategy;
- //set to null after post-initialization
- this.reflectionManager = reflectionManager;
- this.similarity = context.getDefaultSimilarity();
-
- init( clazz, context, reflectionManager );
- }
-
- /**
- * Constructor used on a non @Indexed entity.
- */
- public DocumentBuilder(XClass clazz, InitContext context, ReflectionManager reflectionManager) {
- this.entityState = EntityState.CONTAINED_IN_ONLY;
- this.beanClass = clazz;
- this.directoryProviders = null;
- this.shardingStrategy = null;
-
-
- this.reflectionManager = reflectionManager;
- this.similarity = context.getDefaultSimilarity();
-
- init( clazz, context, reflectionManager );
- if ( rootPropertiesMetadata.containedInGetters.size() == 0 ) {
- this.entityState = EntityState.NON_INDEXABLE;
- }
- }
-
- private void init(XClass clazz, InitContext context, ReflectionManager reflectionManager) {
- if ( clazz == null ) throw new AssertionFailure( "Unable to build a DocumentBuilder with a null class" );
- rootPropertiesMetadata.boost = getBoost( clazz );
- rootPropertiesMetadata.analyzer = context.getDefaultAnalyzer();
- Set<XClass> processedClasses = new HashSet<XClass>();
- processedClasses.add( clazz );
- initializeMembers( clazz, rootPropertiesMetadata, true, "", processedClasses, context );
- //processedClasses.remove( clazz ); for the sake of completness
- this.analyzer.setGlobalAnalyzer( rootPropertiesMetadata.analyzer );
- if ( entityState == EntityState.INDEXED && idKeywordName == null ) {
- // if no DocumentId then check if we have a ProvidedId instead
- ProvidedId provided = findProvidedId( clazz, reflectionManager );
- if ( provided == null ) throw new SearchException( "No document id in: " + clazz.getName() );
-
- idBridge = BridgeFactory.extractTwoWayType( provided.bridge() );
- idKeywordName = provided.name();
- }
-
- //if composite id, use of (a, b) in ((1,2)TwoWayString2FieldBridgeAdaptor, (3,4)) fails on most database
- //a TwoWayString2FieldBridgeAdaptor is never a composite id
- safeFromTupleId = entityState != EntityState.INDEXED || TwoWayString2FieldBridgeAdaptor.class.isAssignableFrom( idBridge.getClass() );
- checkAllowFieldSelection();
- if ( log.isDebugEnabled() ) {
- log.debug( "Field selection in projections is set to {} for entity {}.", allowFieldSelectionInProjection, clazz.getName() );
- }
- }
-
- /**
- * Checks whether all involved bridges are two way string bridges. If so we can optimize document retrieval
- * by using <code>FieldSelector</code>. See HSEARCH-213.
- */
- private void checkAllowFieldSelection() {
- allowFieldSelectionInProjection = true;
- if ( ! (idBridge instanceof TwoWayStringBridge || idBridge instanceof TwoWayString2FieldBridgeAdaptor) ) {
- allowFieldSelectionInProjection = false;
- return;
- }
- for ( FieldBridge bridge : rootPropertiesMetadata.fieldBridges) {
- if ( !( bridge instanceof TwoWayStringBridge || bridge instanceof TwoWayString2FieldBridgeAdaptor ) ) {
- allowFieldSelectionInProjection = false;
- return;
- }
- }
- }
-
- public boolean isRoot() {
- return isRoot;
- }
-
- public boolean allowFieldSelectionInProjection() {
- return allowFieldSelectionInProjection;
- }
-
- private ProvidedId findProvidedId(XClass clazz, ReflectionManager reflectionManager) {
- ProvidedId id = null;
- XClass currentClass = clazz;
- while ( id == null && ( !reflectionManager.equals( currentClass, Object.class ) ) ) {
- id = currentClass.getAnnotation( ProvidedId.class );
- currentClass = clazz.getSuperclass();
- }
- return id;
- }
-
- private Analyzer getAnalyzer(XAnnotatedElement annotatedElement, InitContext context) {
- org.hibernate.search.annotations.Analyzer analyzerAnn =
- annotatedElement.getAnnotation( org.hibernate.search.annotations.Analyzer.class );
- return getAnalyzer( analyzerAnn, context );
- }
-
- private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn, InitContext context) {
- Class analyzerClass = analyzerAnn == null ? void.class : analyzerAnn.impl();
- if ( analyzerClass == void.class ) {
- String definition = analyzerAnn == null ? "" : analyzerAnn.definition();
- if ( StringHelper.isEmpty( definition ) ) {
- return null;
- }
- else {
-
- return context.buildLazyAnalyzer( definition );
- }
- }
- else {
- try {
- return (Analyzer) analyzerClass.newInstance();
- }
- catch (ClassCastException e) {
- throw new SearchException(
- "Lucene analyzer does not implement " + Analyzer.class.getName() + ": " + analyzerClass.getName(), e
- );
- }
- catch (Exception e) {
- throw new SearchException( "Failed to instantiate lucene analyzer with type " + analyzerClass.getName(), e );
- }
- }
- }
-
- private void initializeMembers(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
- Set<XClass> processedClasses, InitContext context) {
- List<XClass> hierarchy = new ArrayList<XClass>();
- for (XClass currClass = clazz; currClass != null; currClass = currClass.getSuperclass()) {
- hierarchy.add( currClass );
- }
- Class similarityClass = null;
- for (int index = hierarchy.size() - 1; index >= 0; index--) {
- XClass currClass = hierarchy.get( index );
- /**
- * Override the default analyzer for the properties if the class hold one
- * That's the reason we go down the hierarchy
- */
- Analyzer analyzer = getAnalyzer( currClass, context );
-
- if ( analyzer != null ) {
- propertiesMetadata.analyzer = analyzer;
- }
- checkForAnalyzerDefs( currClass, context );
- // Check for any ClassBridges annotation.
- ClassBridges classBridgesAnn = currClass.getAnnotation( ClassBridges.class );
- if ( classBridgesAnn != null ) {
- ClassBridge[] cbs = classBridgesAnn.value();
- for (ClassBridge cb : cbs) {
- bindClassAnnotation( prefix, propertiesMetadata, cb, context );
- }
- }
-
- // Check for any ClassBridge style of annotations.
- ClassBridge classBridgeAnn = currClass.getAnnotation( ClassBridge.class );
- if ( classBridgeAnn != null ) {
- bindClassAnnotation( prefix, propertiesMetadata, classBridgeAnn, context );
- }
-
- //Get similarity
- //TODO: similarity form @IndexedEmbedded are not taken care of. Exception??
- if ( isRoot ) {
- org.hibernate.search.annotations.Similarity similarityAnn = currClass.getAnnotation( org.hibernate.search.annotations.Similarity.class );
- if ( similarityAnn != null ) {
- if ( similarityClass != null ) {
- throw new SearchException( "Multiple Similarities defined in the same class hierarchy: " + beanClass.getName() );
- }
- similarityClass = similarityAnn.impl();
- }
- }
-
- //rejecting non properties (ie regular methods) because the object is loaded from Hibernate,
- // so indexing a non property does not make sense
- List<XProperty> methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
- for (XProperty method : methods) {
- initializeMember( method, propertiesMetadata, isRoot, prefix, processedClasses, context );
- }
-
- List<XProperty> fields = currClass.getDeclaredProperties( XClass.ACCESS_FIELD );
- for (XProperty field : fields) {
- initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses, context );
- }
- }
- if ( isRoot && similarityClass != null ) {
- try {
- similarity = (Similarity) similarityClass.newInstance();
- }
- catch (Exception e) {
- log.error( "Exception attempting to instantiate Similarity '{}' set for {}",
- similarityClass.getName(), beanClass.getName() );
- }
- }
- }
-
- private void checkForAnalyzerDefs(XAnnotatedElement annotatedElement, InitContext context) {
- AnalyzerDefs defs = annotatedElement.getAnnotation( AnalyzerDefs.class );
- if ( defs != null ) {
- for (AnalyzerDef def : defs.value()) {
- context.addAnalyzerDef( def );
- }
- }
- AnalyzerDef def = annotatedElement.getAnnotation( AnalyzerDef.class );
- context.addAnalyzerDef( def );
- }
-
- public String getIdentifierName() {
- return idGetter.getName();
- }
-
- public Similarity getSimilarity() {
- return similarity;
- }
-
- private void initializeMember(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
- String prefix, Set<XClass> processedClasses, InitContext context) {
-
- 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 );
- }
- }
- }
-
- 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 );
- }
- }
-
- 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;
- int potentialLevel = embeddedAnn.depth() + level;
- if ( potentialLevel < 0 ) {
- potentialLevel = Integer.MAX_VALUE;
- }
- maxLevel = potentialLevel > maxLevel ? maxLevel : potentialLevel;
- level++;
-
- XClass elementClass;
- if ( void.class == embeddedAnn.targetElement() ) {
- elementClass = member.getElementClass();
- }
- else {
- elementClass = reflectionManager.toXClass( embeddedAnn.targetElement() );
- }
- if ( maxLevel == Integer.MAX_VALUE //infinite
- && processedClasses.contains( elementClass ) ) {
- throw new SearchException(
- "Circular reference. Duplicate use of "
- + elementClass.getName()
- + " in root entity " + beanClass.getName()
- + "#" + buildEmbeddedPrefix( prefix, embeddedAnn, member )
- );
- }
- if ( level <= maxLevel ) {
- processedClasses.add( elementClass ); //push
-
- setAccessible( member );
- propertiesMetadata.embeddedGetters.add( member );
- PropertiesMetadata metadata = new PropertiesMetadata();
- propertiesMetadata.embeddedPropertiesMetadata.add( metadata );
- metadata.boost = getBoost( member, null );
- //property > entity analyzer
- Analyzer analyzer = getAnalyzer( member, context );
- metadata.analyzer = analyzer != null ? analyzer : propertiesMetadata.analyzer;
- String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
- initializeMembers( elementClass, metadata, false, localPrefix, processedClasses, context );
- /**
- * We will only index the "expected" type but that's OK, HQL cannot do downcasting either
- */
- if ( member.isArray() ) {
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.ARRAY );
- }
- else if ( member.isCollection() ) {
- if ( Map.class.equals( member.getCollectionClass() ) ) {
- //hum subclasses etc etc??
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.MAP );
- }
- else {
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.COLLECTION );
- }
- }
- else {
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.OBJECT );
- }
-
- processedClasses.remove( elementClass ); //pop
- }
- else if ( log.isTraceEnabled() ) {
- String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
- log.trace( "depth reached, ignoring {}", localPrefix );
- }
-
- level--;
- maxLevel = oldMaxLevel; //set back the the old max level
- }
- }
-
- 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();
- propertiesMetadata.classNames.add( fieldName );
- propertiesMetadata.classStores.add( getStore( ann.store() ) );
- propertiesMetadata.classIndexes.add( getIndex( ann.index() ) );
- propertiesMetadata.classTermVectors.add( getTermVector( ann.termVector() ) );
- propertiesMetadata.classBridges.add( BridgeFactory.extractType( ann ) );
- propertiesMetadata.classBoosts.add( ann.boost().value() );
-
- Analyzer analyzer = getAnalyzer( ann.analyzer(), context );
- if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
- if ( analyzer == null ) throw new AssertionFailure( "Analyzer should not be undefined" );
- this.analyzer.addScopedAnalyzer( fieldName, analyzer );
- }
-
- private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn, InitContext context) {
- setAccessible( member );
- propertiesMetadata.fieldGetters.add( member );
- String fieldName = prefix + BinderHelper.getAttributeName( member, fieldAnn.name() );
- propertiesMetadata.fieldNames.add( fieldName );
- propertiesMetadata.fieldStore.add( getStore( fieldAnn.store() ) );
- propertiesMetadata.fieldIndex.add( getIndex( fieldAnn.index() ) );
- propertiesMetadata.fieldBoosts.add( getBoost(member, fieldAnn) );
- propertiesMetadata.fieldTermVectors.add( getTermVector( fieldAnn.termVector() ) );
- propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( fieldAnn, member, reflectionManager ) );
-
- // Field > property > entity analyzer
- Analyzer analyzer = getAnalyzer( fieldAnn.analyzer(), context );
- if ( analyzer == null ) analyzer = getAnalyzer( member, context );
- if ( analyzer != null ) {
- this.analyzer.addScopedAnalyzer( fieldName, analyzer );
- }
- }
-
- private Float getBoost(XProperty member, org.hibernate.search.annotations.Field fieldAnn) {
- float computedBoost = 1.0f;
- Boost boostAnn = member.getAnnotation( Boost.class );
- if (boostAnn != null) computedBoost = boostAnn.value();
- if (fieldAnn != null) computedBoost *= fieldAnn.boost().value();
- return computedBoost;
- }
-
- private String buildEmbeddedPrefix(String prefix, IndexedEmbedded embeddedAnn, XProperty member) {
- String localPrefix = prefix;
- if ( ".".equals( embeddedAnn.prefix() ) ) {
- //default to property name
- localPrefix += member.getName() + '.';
- }
- else {
- localPrefix += embeddedAnn.prefix();
- }
- return localPrefix;
- }
-
- private Field.Store getStore(Store store) {
- switch ( store ) {
- case NO:
- return Field.Store.NO;
- case YES:
- return Field.Store.YES;
- case COMPRESS:
- return Field.Store.COMPRESS;
- default:
- throw new AssertionFailure( "Unexpected Store: " + store );
- }
- }
-
- private Field.TermVector getTermVector(TermVector vector) {
- switch ( vector ) {
- case NO:
- return Field.TermVector.NO;
- case YES:
- return Field.TermVector.YES;
- case WITH_OFFSETS:
- return Field.TermVector.WITH_OFFSETS;
- case WITH_POSITIONS:
- return Field.TermVector.WITH_POSITIONS;
- case WITH_POSITION_OFFSETS:
- return Field.TermVector.WITH_POSITIONS_OFFSETS;
- default:
- throw new AssertionFailure( "Unexpected TermVector: " + vector );
- }
- }
-
- private Field.Index getIndex(Index index) {
- switch ( index ) {
- case NO:
- return Field.Index.NO;
- case NO_NORMS:
- return Field.Index.NOT_ANALYZED_NO_NORMS;
- case TOKENIZED:
- return Field.Index.ANALYZED;
- case UN_TOKENIZED:
- return Field.Index.NOT_ANALYZED;
- default:
- throw new AssertionFailure( "Unexpected Index: " + index );
- }
- }
-
- private Float getBoost(XClass element) {
- if ( element == null ) return null;
- Boost boost = element.getAnnotation( Boost.class );
- return boost != null ?
- boost.value() :
- null;
- }
-
- private Object getMemberValue(Object bean, XMember getter) {
- Object value;
- try {
- value = getter.invoke( bean );
- }
- catch (Exception e) {
- throw new IllegalStateException( "Could not get property value", e );
- }
- return value;
- }
-
- //TODO could we use T instead of EntityClass?
- public void addWorkToQueue(Class<T> entityClass, T entity, Serializable id, WorkType workType, List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor) {
- //TODO with the caller loop we are in a n^2: optimize it using a HashMap for work recognition
- if ( entityState == EntityState.INDEXED ) {
- List<LuceneWork> toDelete = new ArrayList<LuceneWork>();
- boolean duplicateDelete = false;
- for (LuceneWork luceneWork : queue) {
- //avoid unecessary duplicated work
- if ( luceneWork.getEntityClass() == entityClass
- ) {
- Serializable currentId = luceneWork.getId();
- //currentId != null => either ADD or Delete work
- if ( currentId != null && currentId.equals( id ) ) { //find a way to use Type.equals(x,y)
- if ( workType == WorkType.DELETE ) { //TODO add PURGE?
- //DELETE should have precedence over any update before (HSEARCH-257)
- //if an Add work is here, remove it
- //if an other delete is here remember but still search for Add
- if ( luceneWork instanceof AddLuceneWork ) {
- toDelete.add( luceneWork );
- }
- else if ( luceneWork instanceof DeleteLuceneWork ) {
- duplicateDelete = true;
- }
- }
- else {
- //we can safely say we are out, the other work is an ADD
- return;
- }
- }
- //TODO do something to avoid multiple PURGE ALL and OPTIMIZE
- }
- }
- for (LuceneWork luceneWork : toDelete) {
- queue.remove( luceneWork );
- }
- if ( duplicateDelete ) return;
-
- String idInString = idBridge.objectToString( id );
- if ( workType == WorkType.ADD ) {
- Document doc = getDocument( entity, id );
- queue.add( new AddLuceneWork( id, idInString, entityClass, doc ) );
- }
- else if ( workType == WorkType.DELETE || workType == WorkType.PURGE ) {
- queue.add( new DeleteLuceneWork( id, idInString, entityClass ) );
- }
- else if ( workType == WorkType.PURGE_ALL ) {
- queue.add( new PurgeAllLuceneWork( entityClass ) );
- }
- else if ( workType == WorkType.UPDATE || workType == WorkType.COLLECTION ) {
- Document doc = getDocument( entity, id );
- /**
- * even with Lucene 2.1, use of indexWriter to update is not an option
- * We can only delete by term, and the index doesn't have a term that
- * uniquely identify the entry.
- * But essentially the optimization we are doing is the same Lucene is doing, the only extra cost is the
- * double file opening.
- */
- queue.add( new DeleteLuceneWork( id, idInString, entityClass ) );
- queue.add( new AddLuceneWork( id, idInString, entityClass, doc ) );
- }
- else if ( workType == WorkType.INDEX ) {
- Document doc = getDocument( entity, id );
- queue.add( new DeleteLuceneWork( id, idInString, entityClass ) );
- queue.add( new AddLuceneWork( id, idInString, entityClass, doc, true ) );
- }
- else {
- throw new AssertionFailure( "Unknown WorkType: " + workType );
- }
- }
-
- /**
- * When references are changed, either null or another one, we expect dirty checking to be triggered (both sides
- * have to be updated)
- * When the internal object is changed, we apply the {Add|Update}Work on containedIns
- */
- if ( workType.searchForContainers() ) {
- processContainedIn( entity, queue, rootPropertiesMetadata, searchFactoryImplementor );
- }
- }
-
- private void processContainedIn(Object instance, List<LuceneWork> queue, PropertiesMetadata metadata, SearchFactoryImplementor searchFactoryImplementor) {
- for (int i = 0; i < metadata.containedInGetters.size(); i++) {
- XMember member = metadata.containedInGetters.get( i );
- Object value = getMemberValue( instance, member );
- if ( value == null ) continue;
-
- if ( member.isArray() ) {
- for (Object arrayValue : (Object[]) value) {
- //highly inneficient but safe wrt the actual targeted class
- Class<?> valueClass = Hibernate.getClass( arrayValue );
- DocumentBuilder<?> builder = searchFactoryImplementor.getDocumentBuilder( valueClass );
- if ( builder == null ) continue;
- processContainedInValue( arrayValue, queue, valueClass, builder, searchFactoryImplementor );
- }
- }
- else if ( member.isCollection() ) {
- Collection collection;
- if ( Map.class.equals( member.getCollectionClass() ) ) {
- //hum
- collection = ( (Map) value ).values();
- }
- else {
- collection = (Collection) value;
- }
- for (Object collectionValue : collection) {
- //highly inneficient but safe wrt the actual targeted class
- Class<?> valueClass = Hibernate.getClass( collectionValue );
- DocumentBuilder<?> builder = searchFactoryImplementor.getDocumentBuilder( valueClass );
- if ( builder == null ) continue;
- processContainedInValue( collectionValue, queue, valueClass, builder, searchFactoryImplementor );
- }
- }
- else {
- Class<?> valueClass = Hibernate.getClass( value );
- DocumentBuilder<?> builder = searchFactoryImplementor.getDocumentBuilder( valueClass );
- if ( builder == null ) continue;
- processContainedInValue( value, queue, valueClass, builder, searchFactoryImplementor );
- }
- }
- //an embedded cannot have a useful @ContainedIn (no shared reference)
- //do not walk through them
- }
-
- private void processContainedInValue(Object value, List<LuceneWork> queue, Class<?> valueClass,
- DocumentBuilder builder, SearchFactoryImplementor searchFactoryImplementor) {
- Serializable id = (Serializable) builder.getMemberValue( value, builder.idGetter );
- builder.addWorkToQueue( valueClass, value, id, WorkType.UPDATE, queue, searchFactoryImplementor );
- }
-
- public Document getDocument(T instance, Serializable id) {
- Document doc = new Document();
- final Class<?> entityType = Hibernate.getClass( instance );
- //XClass instanceClass = reflectionManager.toXClass( entityType );
- if ( rootPropertiesMetadata.boost != null ) {
- doc.setBoost( rootPropertiesMetadata.boost );
- }
- {
- Field classField =
- new Field( CLASS_FIELDNAME, entityType.getName(), Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO );
- doc.add( classField );
- LuceneOptions luceneOptions = new LuceneOptionsImpl( Field.Store.YES,
- Field.Index.NOT_ANALYZED, Field.TermVector.NO, idBoost );
- idBridge.set( idKeywordName, id, doc, luceneOptions );
- }
- buildDocumentFields( instance, doc, rootPropertiesMetadata );
- return doc;
- }
-
- private void buildDocumentFields(Object instance, Document doc, PropertiesMetadata propertiesMetadata) {
- if ( instance == null ) return;
- //needed for field access: I cannot work in the proxied version
- Object unproxiedInstance = unproxy( instance );
- for (int i = 0; i < propertiesMetadata.classBridges.size(); i++) {
- FieldBridge fb = propertiesMetadata.classBridges.get( i );
- fb.set( propertiesMetadata.classNames.get( i ), unproxiedInstance,
- doc, propertiesMetadata.getClassLuceneOptions( i ) );
- }
- for (int i = 0; i < propertiesMetadata.fieldNames.size(); i++) {
- XMember member = propertiesMetadata.fieldGetters.get( i );
- Object value = getMemberValue( unproxiedInstance, member );
- propertiesMetadata.fieldBridges.get( i ).set(
- propertiesMetadata.fieldNames.get( i ), value, doc,
- propertiesMetadata.getFieldLuceneOptions( i ) );
- }
- for (int i = 0; i < propertiesMetadata.embeddedGetters.size(); i++) {
- XMember member = propertiesMetadata.embeddedGetters.get( i );
- Object value = getMemberValue( unproxiedInstance, member );
- //TODO handle boost at embedded level: already stored in propertiesMedatada.boost
-
- if ( value == null ) continue;
- PropertiesMetadata embeddedMetadata = propertiesMetadata.embeddedPropertiesMetadata.get( i );
- switch ( propertiesMetadata.embeddedContainers.get( i ) ) {
- case ARRAY:
- for (Object arrayValue : (Object[]) value) {
- buildDocumentFields( arrayValue, doc, embeddedMetadata );
- }
- break;
- case COLLECTION:
- for (Object collectionValue : (Collection) value) {
- buildDocumentFields( collectionValue, doc, embeddedMetadata );
- }
- break;
- case MAP:
- for (Object collectionValue : ( (Map) value ).values()) {
- buildDocumentFields( collectionValue, doc, embeddedMetadata );
- }
- break;
- case OBJECT:
- buildDocumentFields( value, doc, embeddedMetadata );
- break;
- default:
- throw new AssertionFailure( "Unknown embedded container: "
- + propertiesMetadata.embeddedContainers.get( i ) );
- }
- }
- }
-
- private Object unproxy(Object value) {
- //FIXME this service should be part of Core?
- if ( value instanceof HibernateProxy ) {
- // .getImplementation() initializes the data by side effect
- value = ( (HibernateProxy) value ).getHibernateLazyInitializer()
- .getImplementation();
- }
- return value;
- }
-
- public Term getTerm(Serializable id) {
- if ( idProvided ) {
- return new Term( idKeywordName, (String) id );
- }
-
- return new Term( idKeywordName, idBridge.objectToString( id ) );
- }
-
- public DirectoryProvider[] getDirectoryProviders() {
- if ( entityState != EntityState.INDEXED ) {
- throw new AssertionFailure( "Contained in only entity: getDirectoryProvider should not have been called." );
- }
- return directoryProviders;
- }
-
- public IndexShardingStrategy getDirectoryProviderSelectionStrategy() {
- if ( entityState != EntityState.INDEXED ) {
- throw new AssertionFailure( "Contained in only entity: getDirectoryProviderSelectionStrategy should not have been called." );
- }
- return shardingStrategy;
- }
-
- public Analyzer getAnalyzer() {
- return analyzer;
- }
-
- private static void setAccessible(XMember member) {
- if ( !Modifier.isPublic( member.getModifiers() ) ) {
- member.setAccessible( true );
- }
- }
-
- public TwoWayFieldBridge getIdBridge() {
- return idBridge;
- }
-
- public String getIdKeywordName() {
- return idKeywordName;
- }
-
- public static Class getDocumentClass(Document document) {
- String className = document.get( DocumentBuilder.CLASS_FIELDNAME );
- try {
- return ReflectHelper.classForName( className );
- }
- catch (ClassNotFoundException e) {
- throw new SearchException( "Unable to load indexed class: " + className, e );
- }
- }
-
- public static Serializable getDocumentId(SearchFactoryImplementor searchFactoryImplementor, Class<?> clazz, Document document) {
- DocumentBuilder<?> builder = searchFactoryImplementor.getDocumentBuilder( clazz );
- if ( builder == null ) throw new SearchException( "No Lucene configuration set up for: " + clazz.getName() );
- return (Serializable) builder.getIdBridge().get( builder.getIdKeywordName(), document );
- }
-
- public static Object[] getDocumentFields(SearchFactoryImplementor searchFactoryImplementor, Class<?> clazz, Document document, String[] fields) {
- DocumentBuilder<?> builder = searchFactoryImplementor.getDocumentBuilder( clazz );
- if ( builder == null ) throw new SearchException( "No Lucene configuration set up for: " + clazz.getName() );
- final int fieldNbr = fields.length;
- Object[] result = new Object[fieldNbr];
-
- if ( builder.idKeywordName != null ) {
- populateResult( builder.idKeywordName, builder.idBridge, Field.Store.YES, fields, result, document );
- }
-
- final PropertiesMetadata metadata = builder.rootPropertiesMetadata;
- processFieldsForProjection( metadata, fields, result, document );
- return result;
- }
-
- private static void processFieldsForProjection(PropertiesMetadata metadata, String[] fields, Object[] result, Document document) {
- final int nbrFoEntityFields = metadata.fieldNames.size();
- for (int index = 0; index < nbrFoEntityFields; index++) {
- populateResult( metadata.fieldNames.get( index ),
- metadata.fieldBridges.get( index ),
- metadata.fieldStore.get( index ),
- fields,
- result,
- document
- );
- }
- final int nbrOfEmbeddedObjects = metadata.embeddedPropertiesMetadata.size();
- for (int index = 0; index < nbrOfEmbeddedObjects; index++) {
- //there is nothing we can do for collections
- if ( metadata.embeddedContainers.get( index ) == PropertiesMetadata.Container.OBJECT ) {
- processFieldsForProjection( metadata.embeddedPropertiesMetadata.get( index ), fields, result, document );
- }
- }
- }
-
- private static void populateResult(String fieldName, FieldBridge fieldBridge, Field.Store store,
- String[] fields, Object[] result, Document document) {
- int matchingPosition = getFieldPosition( fields, fieldName );
- if ( matchingPosition != -1 ) {
- //TODO make use of an isTwoWay() method
- if ( store != Field.Store.NO && TwoWayFieldBridge.class.isAssignableFrom( fieldBridge.getClass() ) ) {
- result[matchingPosition] = ( (TwoWayFieldBridge) fieldBridge ).get( fieldName, document );
- if ( log.isTraceEnabled() ) {
- log.trace( "Field {} projected as {}", fieldName, result[matchingPosition] );
- }
- }
- else {
- if ( store == Field.Store.NO ) {
- throw new SearchException( "Projecting an unstored field: " + fieldName );
- }
- else {
- throw new SearchException( "FieldBridge is not a TwoWayFieldBridge: " + fieldBridge.getClass() );
- }
- }
- }
- }
-
- private static int getFieldPosition(String[] fields, String fieldName) {
- int fieldNbr = fields.length;
- for (int index = 0; index < fieldNbr; index++) {
- if ( fieldName.equals( fields[index] ) ) return index;
- }
- return -1;
- }
-
- public void postInitialize(Set<Class<?>> indexedClasses) {
- if ( entityState == EntityState.NON_INDEXABLE )
- throw new AssertionFailure( "A non indexed entity is post processed" );
- //this method does not requires synchronization
- Class plainClass = reflectionManager.toClass( beanClass );
- Set<Class<?>> tempMappedSubclasses = new HashSet<Class<?>>();
- //together with the caller this creates a o(2), but I think it's still faster than create the up hierarchy for each class
- for (Class currentClass : indexedClasses) {
- if ( plainClass.isAssignableFrom( currentClass ) ) tempMappedSubclasses.add( currentClass );
- }
- this.mappedSubclasses = Collections.unmodifiableSet( tempMappedSubclasses );
- Class superClass = plainClass.getSuperclass();
- this.isRoot = true;
- while ( superClass != null ) {
- if ( indexedClasses.contains( superClass ) ) {
- this.isRoot = false;
- break;
- }
- superClass = superClass.getSuperclass();
- }
- this.reflectionManager = null;
- }
-
- public EntityState getEntityState() {
- return entityState;
- }
-
- public Set<Class<?>> getMappedSubclasses() {
- return mappedSubclasses;
- }
-
- /**
- * Make sure to return false if there is a risk of composite id
- * if composite id, use of (a, b) in ((1,2), (3,4)) fails on most database
- */
- public boolean isSafeFromTupleId() {
- return safeFromTupleId;
- }
-
- /**
- * Wrapper class containing all the meta data extracted out of the entities.
- */
- private static class PropertiesMetadata {
- public Float boost;
- public Analyzer analyzer;
- public final List<String> fieldNames = new ArrayList<String>();
- public final List<XMember> fieldGetters = new ArrayList<XMember>();
- public final List<FieldBridge> fieldBridges = new ArrayList<FieldBridge>();
- public final List<Field.Store> fieldStore = new ArrayList<Field.Store>();
- public final List<Field.Index> fieldIndex = new ArrayList<Field.Index>();
- public final List<Float> fieldBoosts = new ArrayList<Float>();
- public final List<Field.TermVector> fieldTermVectors = new ArrayList<Field.TermVector>();
- public final List<XMember> embeddedGetters = new ArrayList<XMember>();
- public final List<PropertiesMetadata> embeddedPropertiesMetadata = new ArrayList<PropertiesMetadata>();
- public final List<Container> embeddedContainers = new ArrayList<Container>();
- public final List<XMember> containedInGetters = new ArrayList<XMember>();
- public final List<String> classNames = new ArrayList<String>();
- public final List<Field.Store> classStores = new ArrayList<Field.Store>();
- public final List<Field.Index> classIndexes = new ArrayList<Field.Index>();
- public final List<FieldBridge> classBridges = new ArrayList<FieldBridge>();
- public final List<Field.TermVector> classTermVectors = new ArrayList<Field.TermVector>();
- public final List<Float> classBoosts = new ArrayList<Float>();
-
- public enum Container {
- OBJECT,
- COLLECTION,
- MAP,
- ARRAY
- }
-
- private LuceneOptions getClassLuceneOptions(int i) {
- LuceneOptions options = new LuceneOptionsImpl( classStores.get( i ),
- classIndexes.get( i ), classTermVectors.get( i ), classBoosts.get( i ) );
- return options;
- }
-
- private LuceneOptions getFieldLuceneOptions(int i) {
- LuceneOptions options;
- options = new LuceneOptionsImpl( fieldStore.get( i ),
- fieldIndex.get( i ), fieldTermVectors.get( i ), fieldBoosts.get( i ) );
- return options;
- }
- }
-}
Copied: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java (from rev 15603, search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java)
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -0,0 +1,1011 @@
+//$Id$
+package org.hibernate.search.engine;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Similarity;
+import org.slf4j.Logger;
+
+import org.hibernate.Hibernate;
+import org.hibernate.annotations.common.AssertionFailure;
+import org.hibernate.annotations.common.reflection.ReflectionManager;
+import org.hibernate.annotations.common.reflection.XAnnotatedElement;
+import org.hibernate.annotations.common.reflection.XClass;
+import org.hibernate.annotations.common.reflection.XMember;
+import org.hibernate.annotations.common.reflection.XProperty;
+import org.hibernate.annotations.common.util.ReflectHelper;
+import org.hibernate.annotations.common.util.StringHelper;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.annotations.AnalyzerDef;
+import org.hibernate.search.annotations.AnalyzerDefs;
+import org.hibernate.search.annotations.Boost;
+import org.hibernate.search.annotations.ClassBridge;
+import org.hibernate.search.annotations.ClassBridges;
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.IndexedEmbedded;
+import org.hibernate.search.annotations.Store;
+import org.hibernate.search.annotations.TermVector;
+import org.hibernate.search.backend.LuceneWork;
+import org.hibernate.search.backend.WorkType;
+import org.hibernate.search.bridge.BridgeFactory;
+import org.hibernate.search.bridge.FieldBridge;
+import org.hibernate.search.bridge.LuceneOptions;
+import org.hibernate.search.bridge.TwoWayFieldBridge;
+import org.hibernate.search.bridge.TwoWayString2FieldBridgeAdaptor;
+import org.hibernate.search.bridge.TwoWayStringBridge;
+import org.hibernate.search.impl.InitContext;
+import org.hibernate.search.util.LoggerFactory;
+import org.hibernate.search.util.ReflectionHelper;
+import org.hibernate.search.util.ScopedAnalyzer;
+
+/**
+ * Set up and provide a manager for indexed classes.
+ *
+ * @author Gavin King
+ * @author Emmanuel Bernard
+ * @author Sylvain Vieujot
+ * @author Richard Hallier
+ * @author Hardy Ferentschik
+ */
+public class DocumentBuilderContainedEntity<T> {
+ private static final Logger log = LoggerFactory.make();
+
+ public static final String CLASS_FIELDNAME = "_hibernate_class";
+
+ protected final PropertiesMetadata metadata = new PropertiesMetadata();
+ protected final XClass beanClass;
+ protected String idKeywordName;
+
+ /**
+ * Flag indicating whether <code>@DocumentId</code> was explicitly specified.
+ */
+ protected boolean explicitDocumentId = false;
+
+ /**
+ * Flag indicating whether {@link org.apache.lucene.search.Searcher#doc(int, org.apache.lucene.document.FieldSelector)}
+ * can be used in order to retrieve documents. This is only safe to do if we know that
+ * all involved bridges are implementing <code>TwoWayStringBridge</code>. See HSEARCH-213.
+ */
+ private boolean allowFieldSelectionInProjection = false;
+
+ protected XMember idGetter;
+ protected Float idBoost;
+ protected TwoWayFieldBridge idBridge;
+ protected Set<Class<?>> mappedSubclasses = new HashSet<Class<?>>();
+ private ReflectionManager reflectionManager; //available only during initializationa nd post-initialization
+ protected int level = 0;
+ protected int maxLevel = Integer.MAX_VALUE;
+ protected final ScopedAnalyzer analyzer = new ScopedAnalyzer();
+ protected Similarity similarity;
+ protected boolean isRoot;
+ //if composite id, use of (a, b) in ((1,2), (3,4)) fails on most database
+ private boolean safeFromTupleId;
+ protected boolean idProvided = false;
+ protected EntityState entityState;
+
+ /**
+ * Constructor used on contained entities not annotated with <code>@Indexed</code> themselves.
+ *
+ * @param clazz The class for which to build a <code>DocumentBuilderContainedEntity</code>.
+ * @param context Handle to default configuration settings.
+ * @param reflectionManager Reflection manager to use for processing the annotations.
+ */
+ public DocumentBuilderContainedEntity(XClass clazz, InitContext context, ReflectionManager reflectionManager) {
+
+ if ( clazz == null ) {
+ throw new AssertionFailure( "Unable to build a DocumentBuilderContainedEntity with a null class" );
+ }
+
+ this.entityState = EntityState.CONTAINED_IN_ONLY;
+ this.beanClass = clazz;
+ this.reflectionManager = reflectionManager;
+
+ init( clazz, context );
+
+ if ( this.similarity == null ) {
+ this.similarity = context.getDefaultSimilarity();
+ }
+
+ if ( metadata.containedInGetters.size() == 0 ) {
+ this.entityState = EntityState.NON_INDEXABLE;
+ }
+ }
+
+ private void init(XClass clazz, InitContext context) {
+ metadata.boost = getBoost( clazz );
+ metadata.analyzer = context.getDefaultAnalyzer();
+
+ Set<XClass> processedClasses = new HashSet<XClass>();
+ processedClasses.add( clazz );
+ initializeMembers( clazz, metadata, true, "", processedClasses, context );
+
+ this.analyzer.setGlobalAnalyzer( metadata.analyzer );
+
+ //if composite id, use of (a, b) in ((1,2)TwoWayString2FieldBridgeAdaptor, (3,4)) fails on most database
+ //a TwoWayString2FieldBridgeAdaptor is never a composite id
+ safeFromTupleId = entityState != EntityState.INDEXED || TwoWayString2FieldBridgeAdaptor.class.isAssignableFrom(
+ idBridge.getClass()
+ );
+
+ checkAllowFieldSelection();
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "Field selection in projections is set to {} for entity {}.",
+ allowFieldSelectionInProjection,
+ clazz.getName()
+ );
+ }
+ }
+
+ /**
+ * Checks whether all involved bridges are two way string bridges. If so we can optimize document retrieval
+ * by using <code>FieldSelector</code>. See HSEARCH-213.
+ */
+ private void checkAllowFieldSelection() {
+ allowFieldSelectionInProjection = true;
+ if ( !( idBridge instanceof TwoWayStringBridge || idBridge instanceof TwoWayString2FieldBridgeAdaptor ) ) {
+ allowFieldSelectionInProjection = false;
+ return;
+ }
+ for ( FieldBridge bridge : metadata.fieldBridges ) {
+ if ( !( bridge instanceof TwoWayStringBridge || bridge instanceof TwoWayString2FieldBridgeAdaptor ) ) {
+ allowFieldSelectionInProjection = false;
+ return;
+ }
+ }
+ }
+
+ public boolean isRoot() {
+ return isRoot;
+ }
+
+ public boolean allowFieldSelectionInProjection() {
+ return allowFieldSelectionInProjection;
+ }
+
+ private Analyzer getAnalyzer(XAnnotatedElement annotatedElement, InitContext context) {
+ org.hibernate.search.annotations.Analyzer analyzerAnn =
+ annotatedElement.getAnnotation( org.hibernate.search.annotations.Analyzer.class );
+ return getAnalyzer( analyzerAnn, context );
+ }
+
+ private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn, InitContext context) {
+ Class analyzerClass = analyzerAnn == null ? void.class : analyzerAnn.impl();
+ if ( analyzerClass == void.class ) {
+ String definition = analyzerAnn == null ? "" : analyzerAnn.definition();
+ if ( StringHelper.isEmpty( definition ) ) {
+ return null;
+ }
+ else {
+
+ return context.buildLazyAnalyzer( definition );
+ }
+ }
+ else {
+ try {
+ return ( Analyzer ) analyzerClass.newInstance();
+ }
+ catch ( ClassCastException e ) {
+ throw new SearchException(
+ "Lucene analyzer does not implement " + Analyzer.class.getName() + ": " + analyzerClass.getName(),
+ e
+ );
+ }
+ catch ( Exception e ) {
+ throw new SearchException(
+ "Failed to instantiate lucene analyzer with type " + analyzerClass.getName(), e
+ );
+ }
+ }
+ }
+
+ private void initializeMembers(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
+ Set<XClass> processedClasses, InitContext context) {
+ List<XClass> hierarchy = new ArrayList<XClass>();
+ for ( XClass currClass = clazz; currClass != null; currClass = currClass.getSuperclass() ) {
+ hierarchy.add( currClass );
+ }
+ for ( int index = hierarchy.size() - 1; index >= 0; index-- ) {
+ XClass currClass = hierarchy.get( index );
+ /*
+ * Override the default analyzer for the properties if the class hold one
+ * That's the reason we go down the hierarchy
+ */
+ Analyzer analyzer = getAnalyzer( currClass, context );
+
+ if ( analyzer != null ) {
+ propertiesMetadata.analyzer = analyzer;
+ }
+ checkForAnalyzerDefs( currClass, context );
+
+ // Check for any ClassBridges annotation.
+ ClassBridges classBridgesAnn = currClass.getAnnotation( ClassBridges.class );
+ if ( classBridgesAnn != null ) {
+ ClassBridge[] cbs = classBridgesAnn.value();
+ for ( ClassBridge cb : cbs ) {
+ bindClassAnnotation( prefix, propertiesMetadata, cb, context );
+ }
+ }
+
+ // Check for any ClassBridge style of annotations.
+ ClassBridge classBridgeAnn = currClass.getAnnotation( ClassBridge.class );
+ if ( classBridgeAnn != null ) {
+ bindClassAnnotation( prefix, propertiesMetadata, classBridgeAnn, context );
+ }
+
+ //Get similarity
+ //TODO: similarity form @IndexedEmbedded are not taken care of. Exception??
+ if ( isRoot ) {
+ checkForSimilarity( currClass );
+ }
+
+ // rejecting non properties (ie regular methods) because the object is loaded from Hibernate,
+ // so indexing a non property does not make sense
+ List<XProperty> methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
+ for ( XProperty method : methods ) {
+ initializeMember( method, propertiesMetadata, isRoot, prefix, processedClasses, context );
+ }
+
+ List<XProperty> fields = currClass.getDeclaredProperties( XClass.ACCESS_FIELD );
+ for ( XProperty field : fields ) {
+ initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses, context );
+ }
+ }
+ }
+
+ private void initializeMember(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
+ String prefix, Set<XClass> processedClasses, InitContext context) {
+ 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 );
+ checkForContainedIn( member, propertiesMetadata );
+ }
+
+ private void checkForAnalyzerDefs(XAnnotatedElement annotatedElement, InitContext context) {
+ AnalyzerDefs defs = annotatedElement.getAnnotation( AnalyzerDefs.class );
+ if ( defs != null ) {
+ for ( AnalyzerDef def : defs.value() ) {
+ context.addAnalyzerDef( def );
+ }
+ }
+ AnalyzerDef def = annotatedElement.getAnnotation( AnalyzerDef.class );
+ context.addAnalyzerDef( def );
+ }
+
+ public String getIdentifierName() {
+ return idGetter.getName();
+ }
+
+ public Similarity getSimilarity() {
+ return similarity;
+ }
+
+ 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 );
+ }
+ }
+ }
+
+ private void checkForSimilarity(XClass currClass) {
+ org.hibernate.search.annotations.Similarity similarityAnn = currClass.getAnnotation( org.hibernate.search.annotations.Similarity.class );
+ if ( similarityAnn != null ) {
+ if ( similarity != null ) {
+ throw new SearchException(
+ "Multiple Similarities defined in the same class hierarchy: " + beanClass.getName()
+ );
+ }
+ Class similarityClass = similarityAnn.impl();
+ try {
+ similarity = ( Similarity ) similarityClass.newInstance();
+ }
+ catch ( Exception e ) {
+ log.error(
+ "Exception attempting to instantiate Similarity '{}' set for {}",
+ similarityClass.getName(), beanClass.getName()
+ );
+ }
+ }
+ }
+
+ 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 );
+ }
+ }
+
+ private void checkForContainedIn(XProperty member, PropertiesMetadata propertiesMetadata) {
+ ContainedIn containedAnn = member.getAnnotation( ContainedIn.class );
+ if ( containedAnn != null ) {
+ ReflectionHelper.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;
+ int potentialLevel = embeddedAnn.depth() + level;
+ if ( potentialLevel < 0 ) {
+ potentialLevel = Integer.MAX_VALUE;
+ }
+ maxLevel = potentialLevel > maxLevel ? maxLevel : potentialLevel;
+ level++;
+
+ XClass elementClass;
+ if ( void.class == embeddedAnn.targetElement() ) {
+ elementClass = member.getElementClass();
+ }
+ else {
+ elementClass = reflectionManager.toXClass( embeddedAnn.targetElement() );
+ }
+ if ( maxLevel == Integer.MAX_VALUE //infinite
+ && processedClasses.contains( elementClass ) ) {
+ throw new SearchException(
+ "Circular reference. Duplicate use of "
+ + elementClass.getName()
+ + " in root entity " + beanClass.getName()
+ + "#" + buildEmbeddedPrefix( prefix, embeddedAnn, member )
+ );
+ }
+ if ( level <= maxLevel ) {
+ processedClasses.add( elementClass ); //push
+
+ ReflectionHelper.setAccessible( member );
+ propertiesMetadata.embeddedGetters.add( member );
+ PropertiesMetadata metadata = new PropertiesMetadata();
+ propertiesMetadata.embeddedPropertiesMetadata.add( metadata );
+ metadata.boost = getBoost( member, null );
+ //property > entity analyzer
+ Analyzer analyzer = getAnalyzer( member, context );
+ metadata.analyzer = analyzer != null ? analyzer : propertiesMetadata.analyzer;
+ String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
+ initializeMembers( elementClass, metadata, false, localPrefix, processedClasses, context );
+ /**
+ * We will only index the "expected" type but that's OK, HQL cannot do downcasting either
+ */
+ if ( member.isArray() ) {
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.ARRAY );
+ }
+ else if ( member.isCollection() ) {
+ if ( Map.class.equals( member.getCollectionClass() ) ) {
+ //hum subclasses etc etc??
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.MAP );
+ }
+ else {
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.COLLECTION );
+ }
+ }
+ else {
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.OBJECT );
+ }
+
+ processedClasses.remove( elementClass ); //pop
+ }
+ else if ( log.isTraceEnabled() ) {
+ String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
+ log.trace( "depth reached, ignoring {}", localPrefix );
+ }
+
+ level--;
+ maxLevel = oldMaxLevel; //set back the the old max level
+ }
+ }
+
+ 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 );
+ ReflectionHelper.setAccessible( member );
+ idGetter = member;
+ }
+ else {
+ //component should index their document id
+ ReflectionHelper.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.
+ * @param context Handle to default configuration settings.
+ *
+ * @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 ReflectionHelper.getAttributeName( member, name );
+ }
+
+ private void bindClassAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann, InitContext context) {
+ //FIXME name should be prefixed
+ String fieldName = prefix + ann.name();
+ propertiesMetadata.classNames.add( fieldName );
+ propertiesMetadata.classStores.add( getStore( ann.store() ) );
+ propertiesMetadata.classIndexes.add( getIndex( ann.index() ) );
+ propertiesMetadata.classTermVectors.add( getTermVector( ann.termVector() ) );
+ propertiesMetadata.classBridges.add( BridgeFactory.extractType( ann ) );
+ propertiesMetadata.classBoosts.add( ann.boost().value() );
+
+ Analyzer analyzer = getAnalyzer( ann.analyzer(), context );
+ if ( analyzer == null ) {
+ analyzer = propertiesMetadata.analyzer;
+ }
+ if ( analyzer == null ) {
+ throw new AssertionFailure( "Analyzer should not be undefined" );
+ }
+ this.analyzer.addScopedAnalyzer( fieldName, analyzer );
+ }
+
+ private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn, InitContext context) {
+ ReflectionHelper.setAccessible( member );
+ propertiesMetadata.fieldGetters.add( member );
+ String fieldName = prefix + ReflectionHelper.getAttributeName( member, fieldAnn.name() );
+ propertiesMetadata.fieldNames.add( fieldName );
+ propertiesMetadata.fieldStore.add( getStore( fieldAnn.store() ) );
+ propertiesMetadata.fieldIndex.add( getIndex( fieldAnn.index() ) );
+ propertiesMetadata.fieldBoosts.add( getBoost( member, fieldAnn ) );
+ propertiesMetadata.fieldTermVectors.add( getTermVector( fieldAnn.termVector() ) );
+ propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( fieldAnn, member, reflectionManager ) );
+
+ // Field > property > entity analyzer
+ Analyzer analyzer = getAnalyzer( fieldAnn.analyzer(), context );
+ if ( analyzer == null ) {
+ analyzer = getAnalyzer( member, context );
+ }
+ if ( analyzer != null ) {
+ this.analyzer.addScopedAnalyzer( fieldName, analyzer );
+ }
+ }
+
+ private Float getBoost(XProperty member, org.hibernate.search.annotations.Field fieldAnn) {
+ float computedBoost = 1.0f;
+ Boost boostAnn = member.getAnnotation( Boost.class );
+ if ( boostAnn != null ) {
+ computedBoost = boostAnn.value();
+ }
+ if ( fieldAnn != null ) {
+ computedBoost *= fieldAnn.boost().value();
+ }
+ return computedBoost;
+ }
+
+ private String buildEmbeddedPrefix(String prefix, IndexedEmbedded embeddedAnn, XProperty member) {
+ String localPrefix = prefix;
+ if ( ".".equals( embeddedAnn.prefix() ) ) {
+ //default to property name
+ localPrefix += member.getName() + '.';
+ }
+ else {
+ localPrefix += embeddedAnn.prefix();
+ }
+ return localPrefix;
+ }
+
+ private Field.Store getStore(Store store) {
+ switch ( store ) {
+ case NO:
+ return Field.Store.NO;
+ case YES:
+ return Field.Store.YES;
+ case COMPRESS:
+ return Field.Store.COMPRESS;
+ default:
+ throw new AssertionFailure( "Unexpected Store: " + store );
+ }
+ }
+
+ private Field.TermVector getTermVector(TermVector vector) {
+ switch ( vector ) {
+ case NO:
+ return Field.TermVector.NO;
+ case YES:
+ return Field.TermVector.YES;
+ case WITH_OFFSETS:
+ return Field.TermVector.WITH_OFFSETS;
+ case WITH_POSITIONS:
+ return Field.TermVector.WITH_POSITIONS;
+ case WITH_POSITION_OFFSETS:
+ return Field.TermVector.WITH_POSITIONS_OFFSETS;
+ default:
+ throw new AssertionFailure( "Unexpected TermVector: " + vector );
+ }
+ }
+
+ private Field.Index getIndex(Index index) {
+ switch ( index ) {
+ case NO:
+ return Field.Index.NO;
+ case NO_NORMS:
+ return Field.Index.NOT_ANALYZED_NO_NORMS;
+ case TOKENIZED:
+ return Field.Index.ANALYZED;
+ case UN_TOKENIZED:
+ return Field.Index.NOT_ANALYZED;
+ default:
+ throw new AssertionFailure( "Unexpected Index: " + index );
+ }
+ }
+
+ private Float getBoost(XClass element) {
+ if ( element == null ) {
+ return null;
+ }
+ Boost boost = element.getAnnotation( Boost.class );
+ return boost != null ?
+ boost.value() :
+ null;
+ }
+
+ //TODO could we use T instead of EntityClass?
+ public void addWorkToQueue(Class<T> entityClass, T entity, Serializable id, WorkType workType, List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor) {
+ /**
+ * When references are changed, either null or another one, we expect dirty checking to be triggered (both sides
+ * have to be updated)
+ * When the internal object is changed, we apply the {Add|Update}Work on containedIns
+ */
+ if ( workType.searchForContainers() ) {
+ processContainedIn( entity, queue, metadata, searchFactoryImplementor );
+ }
+ }
+
+ protected void processContainedIn(Object instance, List<LuceneWork> queue, PropertiesMetadata metadata, SearchFactoryImplementor searchFactoryImplementor) {
+ for ( int i = 0; i < metadata.containedInGetters.size(); i++ ) {
+ XMember member = metadata.containedInGetters.get( i );
+ Object value = ReflectionHelper.getMemberValue( instance, member );
+ if ( value == null ) {
+ continue;
+ }
+
+ if ( member.isArray() ) {
+ for ( Object arrayValue : ( Object[] ) value ) {
+ //highly inneficient but safe wrt the actual targeted class
+ Class<?> valueClass = Hibernate.getClass( arrayValue );
+ DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity( valueClass );
+ if ( builderIndexedEntity == null ) {
+ continue;
+ }
+ processContainedInValue( arrayValue, queue, valueClass,
+ builderIndexedEntity, searchFactoryImplementor );
+ }
+ }
+ else if ( member.isCollection() ) {
+ Collection collection;
+ if ( Map.class.equals( member.getCollectionClass() ) ) {
+ //hum
+ collection = ( ( Map ) value ).values();
+ }
+ else {
+ collection = ( Collection ) value;
+ }
+ for ( Object collectionValue : collection ) {
+ //highly inneficient but safe wrt the actual targeted class
+ Class<?> valueClass = Hibernate.getClass( collectionValue );
+ DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity( valueClass );
+ if ( builderIndexedEntity == null ) {
+ continue;
+ }
+ processContainedInValue( collectionValue, queue, valueClass,
+ builderIndexedEntity, searchFactoryImplementor );
+ }
+ }
+ else {
+ Class<?> valueClass = Hibernate.getClass( value );
+ DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity( valueClass );
+ if ( builderIndexedEntity == null ) {
+ continue;
+ }
+ processContainedInValue( value, queue, valueClass, builderIndexedEntity, searchFactoryImplementor );
+ }
+ }
+ //an embedded cannot have a useful @ContainedIn (no shared reference)
+ //do not walk through them
+ }
+
+ private void processContainedInValue(Object value, List<LuceneWork> queue, Class<?> valueClass,
+ DocumentBuilderIndexedEntity builderIndexedEntity, SearchFactoryImplementor searchFactoryImplementor) {
+ Serializable id = ( Serializable ) ReflectionHelper.getMemberValue( value, builderIndexedEntity.idGetter );
+ builderIndexedEntity.addWorkToQueue( valueClass, value, id, WorkType.UPDATE, queue, searchFactoryImplementor );
+ }
+
+ public Document getDocument(T instance, Serializable id) {
+ Document doc = new Document();
+ final Class<?> entityType = Hibernate.getClass( instance );
+ //XClass instanceClass = reflectionManager.toXClass( entityType );
+ if ( metadata.boost != null ) {
+ doc.setBoost( metadata.boost );
+ }
+ {
+ Field classField =
+ new Field(
+ CLASS_FIELDNAME,
+ entityType.getName(),
+ Field.Store.YES,
+ Field.Index.NOT_ANALYZED,
+ Field.TermVector.NO
+ );
+ doc.add( classField );
+ LuceneOptions luceneOptions = new LuceneOptionsImpl(
+ Field.Store.YES,
+ Field.Index.NOT_ANALYZED, Field.TermVector.NO, idBoost
+ );
+ idBridge.set( idKeywordName, id, doc, luceneOptions );
+ }
+ buildDocumentFields( instance, doc, metadata );
+ return doc;
+ }
+
+ private void buildDocumentFields(Object instance, Document doc, PropertiesMetadata propertiesMetadata) {
+ if ( instance == null ) {
+ return;
+ }
+ //needed for field access: I cannot work in the proxied version
+ Object unproxiedInstance = unproxy( instance );
+ for ( int i = 0; i < propertiesMetadata.classBridges.size(); i++ ) {
+ FieldBridge fb = propertiesMetadata.classBridges.get( i );
+ fb.set(
+ propertiesMetadata.classNames.get( i ), unproxiedInstance,
+ doc, propertiesMetadata.getClassLuceneOptions( i )
+ );
+ }
+ for ( int i = 0; i < propertiesMetadata.fieldNames.size(); i++ ) {
+ XMember member = propertiesMetadata.fieldGetters.get( i );
+ Object value = ReflectionHelper.getMemberValue( unproxiedInstance, member );
+ propertiesMetadata.fieldBridges.get( i ).set(
+ propertiesMetadata.fieldNames.get( i ), value, doc,
+ propertiesMetadata.getFieldLuceneOptions( i )
+ );
+ }
+ for ( int i = 0; i < propertiesMetadata.embeddedGetters.size(); i++ ) {
+ XMember member = propertiesMetadata.embeddedGetters.get( i );
+ Object value = ReflectionHelper.getMemberValue( unproxiedInstance, member );
+ //TODO handle boost at embedded level: already stored in propertiesMedatada.boost
+
+ if ( value == null ) {
+ continue;
+ }
+ PropertiesMetadata embeddedMetadata = propertiesMetadata.embeddedPropertiesMetadata.get( i );
+ switch ( propertiesMetadata.embeddedContainers.get( i ) ) {
+ case ARRAY:
+ for ( Object arrayValue : ( Object[] ) value ) {
+ buildDocumentFields( arrayValue, doc, embeddedMetadata );
+ }
+ break;
+ case COLLECTION:
+ for ( Object collectionValue : ( Collection ) value ) {
+ buildDocumentFields( collectionValue, doc, embeddedMetadata );
+ }
+ break;
+ case MAP:
+ for ( Object collectionValue : ( ( Map ) value ).values() ) {
+ buildDocumentFields( collectionValue, doc, embeddedMetadata );
+ }
+ break;
+ case OBJECT:
+ buildDocumentFields( value, doc, embeddedMetadata );
+ break;
+ default:
+ throw new AssertionFailure(
+ "Unknown embedded container: "
+ + propertiesMetadata.embeddedContainers.get( i )
+ );
+ }
+ }
+ }
+
+ private Object unproxy(Object value) {
+ //FIXME this service should be part of Core?
+ if ( value instanceof HibernateProxy ) {
+ // .getImplementation() initializes the data by side effect
+ value = ( ( HibernateProxy ) value ).getHibernateLazyInitializer()
+ .getImplementation();
+ }
+ return value;
+ }
+
+ public Term getTerm(Serializable id) {
+ if ( idProvided ) {
+ return new Term( idKeywordName, ( String ) id );
+ }
+
+ return new Term( idKeywordName, idBridge.objectToString( id ) );
+ }
+
+ public Analyzer getAnalyzer() {
+ return analyzer;
+ }
+
+ public TwoWayFieldBridge getIdBridge() {
+ return idBridge;
+ }
+
+ public String getIdKeywordName() {
+ return idKeywordName;
+ }
+
+ public static Class getDocumentClass(Document document) {
+ String className = document.get( DocumentBuilderContainedEntity.CLASS_FIELDNAME );
+ try {
+ return ReflectHelper.classForName( className );
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new SearchException( "Unable to load indexed class: " + className, e );
+ }
+ }
+
+ public static Serializable getDocumentId(SearchFactoryImplementor searchFactoryImplementor, Class<?> clazz, Document document) {
+ DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity( clazz );
+ if ( builderIndexedEntity == null ) {
+ throw new SearchException( "No Lucene configuration set up for: " + clazz.getName() );
+ }
+ return ( Serializable ) builderIndexedEntity.getIdBridge().get( builderIndexedEntity.getIdKeywordName(), document );
+ }
+
+ public static Object[] getDocumentFields(SearchFactoryImplementor searchFactoryImplementor, Class<?> clazz, Document document, String[] fields) {
+ DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity( clazz );
+ if ( builderIndexedEntity == null ) {
+ throw new SearchException( "No Lucene configuration set up for: " + clazz.getName() );
+ }
+ final int fieldNbr = fields.length;
+ Object[] result = new Object[fieldNbr];
+
+ if ( builderIndexedEntity.idKeywordName != null ) {
+ populateResult( builderIndexedEntity.idKeywordName, builderIndexedEntity.idBridge, Field.Store.YES, fields, result, document );
+ }
+
+ final PropertiesMetadata metadata = builderIndexedEntity.metadata;
+ processFieldsForProjection( metadata, fields, result, document );
+ return result;
+ }
+
+ private static void processFieldsForProjection(PropertiesMetadata metadata, String[] fields, Object[] result, Document document) {
+ final int nbrFoEntityFields = metadata.fieldNames.size();
+ for ( int index = 0; index < nbrFoEntityFields; index++ ) {
+ populateResult(
+ metadata.fieldNames.get( index ),
+ metadata.fieldBridges.get( index ),
+ metadata.fieldStore.get( index ),
+ fields,
+ result,
+ document
+ );
+ }
+ final int nbrOfEmbeddedObjects = metadata.embeddedPropertiesMetadata.size();
+ for ( int index = 0; index < nbrOfEmbeddedObjects; index++ ) {
+ //there is nothing we can do for collections
+ if ( metadata.embeddedContainers.get( index ) == PropertiesMetadata.Container.OBJECT ) {
+ processFieldsForProjection(
+ metadata.embeddedPropertiesMetadata.get( index ), fields, result, document
+ );
+ }
+ }
+ }
+
+ private static void populateResult(String fieldName, FieldBridge fieldBridge, Field.Store store,
+ String[] fields, Object[] result, Document document) {
+ int matchingPosition = getFieldPosition( fields, fieldName );
+ if ( matchingPosition != -1 ) {
+ //TODO make use of an isTwoWay() method
+ if ( store != Field.Store.NO && TwoWayFieldBridge.class.isAssignableFrom( fieldBridge.getClass() ) ) {
+ result[matchingPosition] = ( ( TwoWayFieldBridge ) fieldBridge ).get( fieldName, document );
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Field {} projected as {}", fieldName, result[matchingPosition] );
+ }
+ }
+ else {
+ if ( store == Field.Store.NO ) {
+ throw new SearchException( "Projecting an unstored field: " + fieldName );
+ }
+ else {
+ throw new SearchException( "FieldBridge is not a TwoWayFieldBridge: " + fieldBridge.getClass() );
+ }
+ }
+ }
+ }
+
+ private static int getFieldPosition(String[] fields, String fieldName) {
+ int fieldNbr = fields.length;
+ for ( int index = 0; index < fieldNbr; index++ ) {
+ if ( fieldName.equals( fields[index] ) ) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ public void postInitialize(Set<Class<?>> indexedClasses) {
+ if ( entityState == EntityState.NON_INDEXABLE ) {
+ throw new AssertionFailure( "A non indexed entity is post processed" );
+ }
+ //this method does not requires synchronization
+ Class plainClass = reflectionManager.toClass( beanClass );
+ Set<Class<?>> tempMappedSubclasses = new HashSet<Class<?>>();
+ //together with the caller this creates a o(2), but I think it's still faster than create the up hierarchy for each class
+ for ( Class currentClass : indexedClasses ) {
+ if ( plainClass.isAssignableFrom( currentClass ) ) {
+ tempMappedSubclasses.add( currentClass );
+ }
+ }
+ this.mappedSubclasses = Collections.unmodifiableSet( tempMappedSubclasses );
+ Class superClass = plainClass.getSuperclass();
+ this.isRoot = true;
+ while ( superClass != null ) {
+ if ( indexedClasses.contains( superClass ) ) {
+ this.isRoot = false;
+ break;
+ }
+ superClass = superClass.getSuperclass();
+ }
+ this.reflectionManager = null;
+ }
+
+ public EntityState getEntityState() {
+ return entityState;
+ }
+
+ public Set<Class<?>> getMappedSubclasses() {
+ return mappedSubclasses;
+ }
+
+ /**
+ * Make sure to return false if there is a risk of composite id
+ * if composite id, use of (a, b) in ((1,2), (3,4)) fails on most database
+ */
+ public boolean isSafeFromTupleId() {
+ return safeFromTupleId;
+ }
+
+ /**
+ * Wrapper class containing all the meta data extracted out of the entities.
+ */
+ protected static class PropertiesMetadata {
+ public Float boost;
+ public Analyzer analyzer;
+ public final List<String> fieldNames = new ArrayList<String>();
+ public final List<XMember> fieldGetters = new ArrayList<XMember>();
+ public final List<FieldBridge> fieldBridges = new ArrayList<FieldBridge>();
+ public final List<Field.Store> fieldStore = new ArrayList<Field.Store>();
+ public final List<Field.Index> fieldIndex = new ArrayList<Field.Index>();
+ public final List<Float> fieldBoosts = new ArrayList<Float>();
+ public final List<Field.TermVector> fieldTermVectors = new ArrayList<Field.TermVector>();
+ public final List<XMember> embeddedGetters = new ArrayList<XMember>();
+ public final List<PropertiesMetadata> embeddedPropertiesMetadata = new ArrayList<PropertiesMetadata>();
+ public final List<Container> embeddedContainers = new ArrayList<Container>();
+ public final List<XMember> containedInGetters = new ArrayList<XMember>();
+ public final List<String> classNames = new ArrayList<String>();
+ public final List<Field.Store> classStores = new ArrayList<Field.Store>();
+ public final List<Field.Index> classIndexes = new ArrayList<Field.Index>();
+ public final List<FieldBridge> classBridges = new ArrayList<FieldBridge>();
+ public final List<Field.TermVector> classTermVectors = new ArrayList<Field.TermVector>();
+ public final List<Float> classBoosts = new ArrayList<Float>();
+
+ public enum Container {
+ OBJECT,
+ COLLECTION,
+ MAP,
+ ARRAY
+ }
+
+ protected LuceneOptions getClassLuceneOptions(int i) {
+ return new LuceneOptionsImpl(
+ classStores.get( i ),
+ classIndexes.get( i ), classTermVectors.get( i ), classBoosts.get( i )
+ );
+ }
+
+ protected LuceneOptions getFieldLuceneOptions(int i) {
+ LuceneOptions options;
+ options = new LuceneOptionsImpl(
+ fieldStore.get( i ),
+ fieldIndex.get( i ), fieldTermVectors.get( i ), fieldBoosts.get( i )
+ );
+ return options;
+ }
+ }
+}
\ No newline at end of file
Property changes on: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:mergeinfo
+
Copied: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java (from rev 15603, search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java)
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -0,0 +1,162 @@
+//$Id$
+package org.hibernate.search.engine;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.document.Document;
+
+import org.hibernate.annotations.common.AssertionFailure;
+import org.hibernate.annotations.common.reflection.ReflectionManager;
+import org.hibernate.annotations.common.reflection.XClass;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.annotations.ProvidedId;
+import org.hibernate.search.backend.AddLuceneWork;
+import org.hibernate.search.backend.DeleteLuceneWork;
+import org.hibernate.search.backend.LuceneWork;
+import org.hibernate.search.backend.PurgeAllLuceneWork;
+import org.hibernate.search.backend.WorkType;
+import org.hibernate.search.bridge.BridgeFactory;
+import org.hibernate.search.impl.InitContext;
+import org.hibernate.search.store.DirectoryProvider;
+import org.hibernate.search.store.IndexShardingStrategy;
+
+/**
+ * Set up and provide a manager for indexed classes.
+ *
+ * @author Gavin King
+ * @author Emmanuel Bernard
+ * @author Sylvain Vieujot
+ * @author Richard Hallier
+ * @author Hardy Ferentschik
+ */
+public class DocumentBuilderIndexedEntity<T> extends DocumentBuilderContainedEntity<T> {
+
+ private final DirectoryProvider[] directoryProviders;
+ private final IndexShardingStrategy shardingStrategy;
+
+ /**
+ * Constructor used on an @Indexed entity.
+ */
+ public DocumentBuilderIndexedEntity(XClass clazz, InitContext context, DirectoryProvider[] directoryProviders,
+ IndexShardingStrategy shardingStrategy, ReflectionManager reflectionManager) {
+
+ super( clazz, context, reflectionManager );
+
+ this.entityState = EntityState.INDEXED;
+ this.directoryProviders = directoryProviders;
+ this.shardingStrategy = shardingStrategy;
+
+ if ( idKeywordName == null ) {
+ // if no DocumentId then check if we have a ProvidedId instead
+ ProvidedId provided = findProvidedId( clazz, reflectionManager );
+ if ( provided == null ) {
+ throw new SearchException( "No document id in: " + clazz.getName() );
+ }
+
+ idBridge = BridgeFactory.extractTwoWayType( provided.bridge() );
+ idKeywordName = provided.name();
+ }
+ }
+
+ private ProvidedId findProvidedId(XClass clazz, ReflectionManager reflectionManager) {
+ ProvidedId id = null;
+ XClass currentClass = clazz;
+ while ( id == null && ( !reflectionManager.equals( currentClass, Object.class ) ) ) {
+ id = currentClass.getAnnotation( ProvidedId.class );
+ currentClass = clazz.getSuperclass();
+ }
+ return id;
+ }
+
+ //TODO could we use T instead of EntityClass?
+ public void addWorkToQueue(Class<T> entityClass, T entity, Serializable id, WorkType workType, List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor) {
+ //TODO with the caller loop we are in a n^2: optimize it using a HashMap for work recognition
+
+ List<LuceneWork> toDelete = new ArrayList<LuceneWork>();
+ boolean duplicateDelete = false;
+ for ( LuceneWork luceneWork : queue ) {
+ //avoid unecessary duplicated work
+ if ( luceneWork.getEntityClass() == entityClass
+ ) {
+ Serializable currentId = luceneWork.getId();
+ //currentId != null => either ADD or Delete work
+ if ( currentId != null && currentId.equals( id ) ) { //find a way to use Type.equals(x,y)
+ if ( workType == WorkType.DELETE ) { //TODO add PURGE?
+ //DELETE should have precedence over any update before (HSEARCH-257)
+ //if an Add work is here, remove it
+ //if an other delete is here remember but still search for Add
+ if ( luceneWork instanceof AddLuceneWork ) {
+ toDelete.add( luceneWork );
+ }
+ else if ( luceneWork instanceof DeleteLuceneWork ) {
+ duplicateDelete = true;
+ }
+ }
+ else {
+ //we can safely say we are out, the other work is an ADD
+ return;
+ }
+ }
+ //TODO do something to avoid multiple PURGE ALL and OPTIMIZE
+ }
+ }
+ for ( LuceneWork luceneWork : toDelete ) {
+ queue.remove( luceneWork );
+ }
+ if ( duplicateDelete ) {
+ return;
+ }
+
+ String idInString = idBridge.objectToString( id );
+ if ( workType == WorkType.ADD ) {
+ Document doc = getDocument( entity, id );
+ queue.add( new AddLuceneWork( id, idInString, entityClass, doc ) );
+ }
+ else if ( workType == WorkType.DELETE || workType == WorkType.PURGE ) {
+ queue.add( new DeleteLuceneWork( id, idInString, entityClass ) );
+ }
+ else if ( workType == WorkType.PURGE_ALL ) {
+ queue.add( new PurgeAllLuceneWork( entityClass ) );
+ }
+ else if ( workType == WorkType.UPDATE || workType == WorkType.COLLECTION ) {
+ Document doc = getDocument( entity, id );
+ /**
+ * even with Lucene 2.1, use of indexWriter to update is not an option
+ * We can only delete by term, and the index doesn't have a term that
+ * uniquely identify the entry.
+ * But essentially the optimization we are doing is the same Lucene is doing, the only extra cost is the
+ * double file opening.
+ */
+ queue.add( new DeleteLuceneWork( id, idInString, entityClass ) );
+ queue.add( new AddLuceneWork( id, idInString, entityClass, doc ) );
+ }
+ else if ( workType == WorkType.INDEX ) {
+ Document doc = getDocument( entity, id );
+ queue.add( new DeleteLuceneWork( id, idInString, entityClass ) );
+ queue.add( new AddLuceneWork( id, idInString, entityClass, doc, true ) );
+ }
+ else {
+ throw new AssertionFailure( "Unknown WorkType: " + workType );
+ }
+
+ super.addWorkToQueue(entityClass, entity, id, workType, queue, searchFactoryImplementor);
+ }
+
+ public DirectoryProvider[] getDirectoryProviders() {
+ if ( entityState != EntityState.INDEXED ) {
+ throw new AssertionFailure( "Contained in only entity: getDirectoryProvider should not have been called." );
+ }
+ return directoryProviders;
+ }
+
+ public IndexShardingStrategy getDirectoryProviderSelectionStrategy() {
+ if ( entityState != EntityState.INDEXED ) {
+ throw new AssertionFailure(
+ "Contained in only entity: getDirectoryProviderSelectionStrategy should not have been called."
+ );
+ }
+ return shardingStrategy;
+ }
+}
Property changes on: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -52,7 +52,7 @@
// set up the field selector. CLASS_FIELDNAME and id fields are needed on top of any projected fields
Map<String, FieldSelectorResult> fields = new HashMap<String, FieldSelectorResult>( 1 + idFieldNames.size() + projectionSize );
- fields.put( DocumentBuilder.CLASS_FIELDNAME, FieldSelectorResult.LOAD );
+ fields.put( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, FieldSelectorResult.LOAD );
for ( String idFieldName : idFieldNames ) {
fields.put( idFieldName, FieldSelectorResult.LOAD );
}
@@ -65,11 +65,11 @@
}
private EntityInfo extract(Document document) {
- Class clazz = DocumentBuilder.getDocumentClass( document );
- Serializable id = DocumentBuilder.getDocumentId( searchFactoryImplementor, clazz, document );
+ Class clazz = DocumentBuilderIndexedEntity.getDocumentClass( document );
+ Serializable id = DocumentBuilderIndexedEntity.getDocumentId( searchFactoryImplementor, clazz, document );
Object[] projected = null;
if ( projection != null && projection.length > 0 ) {
- projected = DocumentBuilder.getDocumentFields( searchFactoryImplementor, clazz, document, projection );
+ projected = DocumentBuilderIndexedEntity.getDocumentFields( searchFactoryImplementor, clazz, document, projection );
}
return new EntityInfo( clazz, id, projected );
}
Modified: search/trunk/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -35,7 +35,7 @@
// root entity could lead to quite inefficient queries in Hibernate when using table per class
if ( entityTypes.size() == 0 ) {
//support all classes
- for( Map.Entry<Class<?>, DocumentBuilder<?>> entry : searchFactoryImplementor.getDocumentBuilders().entrySet() ) {
+ for( Map.Entry<Class<?>, DocumentBuilderIndexedEntity<?>> entry : searchFactoryImplementor.getDocumentBuilders().entrySet() ) {
//get only root entities to limit queries
if ( entry.getValue().isRoot() ) {
safeEntityTypes.add( entry.getKey() );
@@ -113,7 +113,7 @@
RootEntityMetadata(Class<?> rootEntity, SearchFactoryImplementor searchFactoryImplementor, Session session) {
this.rootEntity = rootEntity;
- DocumentBuilder<?> provider = searchFactoryImplementor.getDocumentBuilder( rootEntity );
+ DocumentBuilderIndexedEntity<?> provider = searchFactoryImplementor.getDocumentBuilderIndexedEntity( rootEntity );
if ( provider == null) throw new AssertionFailure("Provider not found for class: " + rootEntity);
this.mappedSubclasses = provider.getMappedSubclasses();
this.criteria = session.createCriteria( rootEntity );
Modified: search/trunk/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -46,7 +46,7 @@
final int maxResults = entityInfos.length;
if ( maxResults == 0 ) return;
- DocumentBuilder<?> builder = searchFactoryImplementor.getDocumentBuilder( entityType );
+ DocumentBuilderIndexedEntity<?> builder = searchFactoryImplementor.getDocumentBuilderIndexedEntity( entityType );
String idName = builder.getIdentifierName();
int loop = maxResults / MAX_IN_CLAUSE;
boolean exact = maxResults % MAX_IN_CLAUSE == 0;
Modified: search/trunk/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -24,11 +24,11 @@
void setBackendQueueProcessorFactory(BackendQueueProcessorFactory backendQueueProcessorFactory);
- Map<Class<?>, DocumentBuilder<?>> getDocumentBuilders();
+ Map<Class<?>, DocumentBuilderIndexedEntity<?>> getDocumentBuilders();
- <T> DocumentBuilder<T> getDocumentBuilder(Class<T> entityType);
+ <T> DocumentBuilderIndexedEntity<T> getDocumentBuilderIndexedEntity(Class<T> entityType);
- <T> DocumentBuilder<T> getContainedInOnlyBuilder(Class<T> entityType);
+ <T> DocumentBuilderContainedEntity<T> getDocumentBuilderContainedEntity(Class<T> entityType);
Worker getWorker();
Modified: search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -74,7 +74,7 @@
if ( used ) {
final Class<?> entityType = event.getEntity().getClass();
if ( searchFactoryImplementor.getDocumentBuilders().containsKey( entityType )
- || searchFactoryImplementor.getContainedInOnlyBuilder( entityType ) != null ) {
+ || searchFactoryImplementor.getDocumentBuilderContainedEntity( entityType ) != null ) {
processWork( event.getEntity(), event.getId(), WorkType.DELETE, event );
}
}
@@ -83,8 +83,8 @@
public void onPostInsert(PostInsertEvent event) {
if ( used ) {
final Object entity = event.getEntity();
- if ( searchFactoryImplementor.getDocumentBuilder( entity.getClass() ) != null
- || searchFactoryImplementor.getContainedInOnlyBuilder( entity.getClass() ) != null ) {
+ if ( searchFactoryImplementor.getDocumentBuilderIndexedEntity( entity.getClass() ) != null
+ || searchFactoryImplementor.getDocumentBuilderContainedEntity( entity.getClass() ) != null ) {
Serializable id = event.getId();
processWork( entity, id, WorkType.ADD, event );
}
@@ -94,8 +94,8 @@
public void onPostUpdate(PostUpdateEvent event) {
if ( used ) {
final Object entity = event.getEntity();
- if ( searchFactoryImplementor.getDocumentBuilder( entity.getClass() ) != null
- || searchFactoryImplementor.getContainedInOnlyBuilder( entity.getClass() ) != null ) {
+ if ( searchFactoryImplementor.getDocumentBuilderIndexedEntity( entity.getClass() ) != null
+ || searchFactoryImplementor.getDocumentBuilderContainedEntity( entity.getClass() ) != null ) {
Serializable id = event.getId();
processWork( entity, id, WorkType.UPDATE, event );
}
@@ -133,8 +133,8 @@
return;
}
if ( used ) {
- if ( searchFactoryImplementor.getDocumentBuilder( entity.getClass() ) != null
- || searchFactoryImplementor.getContainedInOnlyBuilder( entity.getClass() ) != null ) {
+ if ( searchFactoryImplementor.getDocumentBuilderIndexedEntity( entity.getClass() ) != null
+ || searchFactoryImplementor.getDocumentBuilderContainedEntity( entity.getClass() ) != null ) {
Serializable id = getId( entity, event );
if ( id == null ) {
log.warn(
Modified: search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -48,7 +48,7 @@
import org.hibernate.search.backend.Work;
import org.hibernate.search.backend.WorkType;
import org.hibernate.search.backend.impl.EventSourceTransactionContext;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.hibernate.search.query.FullTextQueryImpl;
import org.hibernate.search.util.ContextHelper;
@@ -62,6 +62,7 @@
* @author John Griffin
* @author Hardy Ferentschik
*/
+@SuppressWarnings( "deprecation" )
public class FullTextSessionImpl implements FullTextSession, SessionImplementor {
private final Session session;
@@ -114,7 +115,7 @@
}
for ( Class clazz : targetedClasses ) {
- DocumentBuilder builder = searchFactoryImplementor.getDocumentBuilder( clazz );
+ DocumentBuilderIndexedEntity builder = searchFactoryImplementor.getDocumentBuilderIndexedEntity( clazz );
Work<T> work;
if ( id == null ) {
// purge the main entity
@@ -153,7 +154,7 @@
//TODO cache that at the FTSession level
SearchFactoryImplementor searchFactoryImplementor = getSearchFactoryImplementor();
//not strictly necessary but a small optimization
- if ( searchFactoryImplementor.getDocumentBuilder( clazz ) == null ) {
+ if ( searchFactoryImplementor.getDocumentBuilderIndexedEntity( clazz ) == null ) {
String msg = "Entity to index is not an @Indexed entity: " + entity.getClass().getName();
throw new IllegalArgumentException( msg );
}
Modified: search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -39,10 +39,11 @@
import org.hibernate.search.backend.WorkerFactory;
import org.hibernate.search.backend.configuration.ConfigurationParseHelper;
import org.hibernate.search.cfg.SearchConfiguration;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.engine.FilterDef;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.hibernate.search.engine.EntityState;
+import org.hibernate.search.engine.DocumentBuilderContainedEntity;
import org.hibernate.search.filter.CachingWrapperFilter;
import org.hibernate.search.filter.FilterCachingStrategy;
import org.hibernate.search.filter.MRUFilterCachingStrategy;
@@ -64,8 +65,8 @@
private static final Logger log = LoggerFactory.make();
- private final Map<Class<?>, DocumentBuilder<?>> documentBuilders = new HashMap<Class<?>, DocumentBuilder<?>>();
- private final Map<Class<?>, DocumentBuilder<?>> containedInOnlyBuilders = new HashMap<Class<?>, DocumentBuilder<?>>();
+ private final Map<Class<?>, DocumentBuilderIndexedEntity<?>> documentBuildersIndexedEntities = new HashMap<Class<?>, DocumentBuilderIndexedEntity<?>>();
+ private final Map<Class<?>, DocumentBuilderContainedEntity<?>> documentBuildersContainedEntities = new HashMap<Class<?>, DocumentBuilderContainedEntity<?>>();
//keep track of the index modifiers per DirectoryProvider since multiple entity can use the same directory provider
private final Map<DirectoryProvider<?>, DirectoryProviderData> dirProviderData = new HashMap<DirectoryProvider<?>, DirectoryProviderData>();
private final Worker worker;
@@ -117,12 +118,12 @@
this.indexingStrategy = defineIndexingStrategy( cfg ); //need to be done before the document builds
initDocumentBuilders( cfg, reflectionManager );
- Set<Class<?>> indexedClasses = documentBuilders.keySet();
- for ( DocumentBuilder builder : documentBuilders.values() ) {
+ Set<Class<?>> indexedClasses = documentBuildersIndexedEntities.keySet();
+ for ( DocumentBuilderIndexedEntity builder : documentBuildersIndexedEntities.values() ) {
builder.postInitialize( indexedClasses );
}
//not really necessary today
- for ( DocumentBuilder builder : containedInOnlyBuilders.values() ) {
+ for ( DocumentBuilderContainedEntity builder : documentBuildersContainedEntities.values() ) {
builder.postInitialize( indexedClasses );
}
this.worker = WorkerFactory.createWorker( cfg, this );
@@ -260,24 +261,24 @@
}
- public Map<Class<?>, DocumentBuilder<?>> getDocumentBuilders() {
+ public Map<Class<?>, DocumentBuilderIndexedEntity<?>> getDocumentBuilders() {
if ( barrier != 0 ) {
} //read barrier
- return documentBuilders;
+ return documentBuildersIndexedEntities;
}
@SuppressWarnings("unchecked")
- public <T> DocumentBuilder<T> getDocumentBuilder(Class<T> entityType) {
+ public <T> DocumentBuilderIndexedEntity<T> getDocumentBuilderIndexedEntity(Class<T> entityType) {
if ( barrier != 0 ) {
} //read barrier
- return ( DocumentBuilder<T> ) documentBuilders.get( entityType );
+ return ( DocumentBuilderIndexedEntity<T> ) documentBuildersIndexedEntities.get( entityType );
}
@SuppressWarnings("unchecked")
- public <T> DocumentBuilder<T> getContainedInOnlyBuilder(Class<T> entityType) {
+ public <T> DocumentBuilderContainedEntity<T> getDocumentBuilderContainedEntity(Class<T> entityType) {
if ( barrier != 0 ) {
} //read barrier
- return ( DocumentBuilder<T> ) containedInOnlyBuilders.get( entityType );
+ return ( DocumentBuilderContainedEntity<T> ) documentBuildersContainedEntities.get( entityType );
}
public Set<DirectoryProvider<?>> getDirectoryProviders() {
@@ -328,7 +329,7 @@
public DirectoryProvider[] getDirectoryProviders(Class<?> entity) {
if ( barrier != 0 ) {
} //read barrier
- DocumentBuilder<?> documentBuilder = getDocumentBuilder( entity );
+ DocumentBuilderIndexedEntity<?> documentBuilder = getDocumentBuilderIndexedEntity( entity );
return documentBuilder == null ? null : documentBuilder.getDirectoryProviders();
}
@@ -367,7 +368,7 @@
throw new IllegalArgumentException( "A class has to be specified for retrieving a scoped analyzer" );
}
- DocumentBuilder<?> builder = documentBuilders.get( clazz );
+ DocumentBuilderIndexedEntity<?> builder = documentBuildersIndexedEntities.get( clazz );
if ( builder == null ) {
throw new IllegalArgumentException(
"Entity for which to retrieve the scoped analyzer is not an @Indexed entity: " + clazz.getName()
@@ -403,25 +404,25 @@
DirectoryProviderFactory.DirectoryProviders providers = factory.createDirectoryProviders(
mappedXClass, cfg, this, reflectionManager
);
- //FIXME DocumentBuilder needs to be built by a helper method receiving Class<T> to infer T properly
+ //FIXME DocumentBuilderIndexedEntity needs to be built by a helper method receiving Class<T> to infer T properly
//XClass unfortunately is not (yet) genericized: TODO?
- final DocumentBuilder<?> documentBuilder = new DocumentBuilder(
+ final DocumentBuilderIndexedEntity<?> documentBuilder = new DocumentBuilderIndexedEntity(
mappedXClass, context, providers.getProviders(), providers.getSelectionStrategy(),
reflectionManager
);
indexHierarchy.addIndexedClass( mappedClass );
- documentBuilders.put( mappedClass, documentBuilder );
+ documentBuildersIndexedEntities.put( mappedClass, documentBuilder );
}
else {
- //FIXME DocumentBuilder needs to be built by a helper method receiving Class<T> to infer T properly
+ //FIXME DocumentBuilderIndexedEntity needs to be built by a helper method receiving Class<T> to infer T properly
//XClass unfortunately is not (yet) genericized: TODO?
- final DocumentBuilder<?> documentBuilder = new DocumentBuilder(
+ final DocumentBuilderContainedEntity<?> documentBuilder = new DocumentBuilderContainedEntity(
mappedXClass, context, reflectionManager
);
//TODO enhance that, I don't like to expose EntityState
if ( documentBuilder.getEntityState() != EntityState.NON_INDEXABLE ) {
- containedInOnlyBuilders.put( mappedClass, documentBuilder );
+ documentBuildersContainedEntities.put( mappedClass, documentBuilder );
}
}
bindFilterDefs( mappedXClass );
Modified: search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -41,7 +41,7 @@
import org.hibernate.search.FullTextFilter;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.SearchException;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.engine.DocumentExtractor;
import org.hibernate.search.engine.EntityInfo;
import org.hibernate.search.engine.FilterDef;
@@ -547,10 +547,10 @@
//A query filter is more practical than a manual class filtering post query (esp on scrollable resultsets)
//it also probably minimise the memory footprint
BooleanQuery classFilter = new BooleanQuery();
- //annihilate the scoring impact of DocumentBuilder.CLASS_FIELDNAME
+ //annihilate the scoring impact of DocumentBuilderIndexedEntity.CLASS_FIELDNAME
classFilter.setBoost( 0 );
for ( Class clazz : classesAndSubclasses ) {
- Term t = new Term( DocumentBuilder.CLASS_FIELDNAME, clazz.getName() );
+ Term t = new Term( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, clazz.getName() );
TermQuery termQuery = new TermQuery( t );
classFilter.add( termQuery, BooleanClause.Occur.SHOULD );
}
@@ -587,7 +587,7 @@
* TODO change classesAndSubclasses by side effect, which is a mismatch with the Searcher return, fix that.
*/
private IndexSearcher buildSearcher(SearchFactoryImplementor searchFactoryImplementor) {
- Map<Class<?>, DocumentBuilder<?>> builders = searchFactoryImplementor.getDocumentBuilders();
+ Map<Class<?>, DocumentBuilderIndexedEntity<?>> builders = searchFactoryImplementor.getDocumentBuilders();
List<DirectoryProvider> directories = new ArrayList<DirectoryProvider>();
Set<String> idFieldNames = new HashSet<String>();
@@ -602,7 +602,7 @@
);
}
- for ( DocumentBuilder builder : builders.values() ) {
+ for ( DocumentBuilderIndexedEntity builder : builders.values() ) {
searcherSimilarity = checkSimilarity( searcherSimilarity, builder );
if ( builder.getIdKeywordName() != null ) {
idFieldNames.add( builder.getIdKeywordName() );
@@ -618,14 +618,14 @@
Set<Class<?>> involvedClasses = new HashSet<Class<?>>( targetedEntities.size() );
involvedClasses.addAll( targetedEntities );
for ( Class<?> clazz : targetedEntities ) {
- DocumentBuilder<?> builder = builders.get( clazz );
+ DocumentBuilderIndexedEntity<?> builder = builders.get( clazz );
if ( builder != null ) {
involvedClasses.addAll( builder.getMappedSubclasses() );
}
}
for ( Class clazz : involvedClasses ) {
- DocumentBuilder builder = builders.get( clazz );
+ DocumentBuilderIndexedEntity builder = builders.get( clazz );
//TODO should we rather choose a polymorphic path and allow non mapped entities
if ( builder == null ) {
throw new HibernateException( "Not a mapped entity (don't forget to add @Indexed): " + clazz );
@@ -685,7 +685,7 @@
}
}
- private Similarity checkSimilarity(Similarity similarity, DocumentBuilder builder) {
+ private Similarity checkSimilarity(Similarity similarity, DocumentBuilderIndexedEntity builder) {
if ( similarity == null ) {
similarity = builder.getSimilarity();
}
Deleted: search/trunk/src/java/org/hibernate/search/util/BinderHelper.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/util/BinderHelper.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/java/org/hibernate/search/util/BinderHelper.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -1,23 +0,0 @@
-//$Id$
-package org.hibernate.search.util;
-
-import org.hibernate.annotations.common.reflection.XMember;
-import org.hibernate.util.StringHelper;
-
-/**
- * @author Emmanuel Bernard
- */
-public abstract class BinderHelper {
-
- private BinderHelper() {
- }
-
- /**
- * Get attribute name out of member unless overriden by <code>name</code>
- */
- public static String getAttributeName(XMember member, String name) {
- return StringHelper.isNotEmpty( name ) ?
- name :
- member.getName(); //explicit field name
- }
-}
Copied: search/trunk/src/java/org/hibernate/search/util/ReflectionHelper.java (from rev 15603, search/trunk/src/java/org/hibernate/search/util/BinderHelper.java)
===================================================================
--- search/trunk/src/java/org/hibernate/search/util/ReflectionHelper.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/util/ReflectionHelper.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -0,0 +1,48 @@
+//$Id$
+package org.hibernate.search.util;
+
+import java.lang.reflect.Modifier;
+
+import org.hibernate.annotations.common.reflection.XMember;
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
+ */
+public abstract class ReflectionHelper {
+
+ private ReflectionHelper() {
+ }
+
+ /**
+ * Get attribute name out of member unless overriden by <code>name</code>.
+ *
+ * @param member <code>XMember</code> from which to extract the name.
+ * @param name Override value which will be returned in case it is not empty.
+ *
+ * @return attribute name out of member unless overriden by <code>name</code>.
+ */
+ public static String getAttributeName(XMember member, String name) {
+ return StringHelper.isNotEmpty( name ) ?
+ name :
+ member.getName(); //explicit field name
+ }
+
+ public static void setAccessible(XMember member) {
+ if ( !Modifier.isPublic( member.getModifiers() ) ) {
+ member.setAccessible( true );
+ }
+ }
+
+ public static Object getMemberValue(Object bean, XMember getter) {
+ Object value;
+ try {
+ value = getter.invoke( bean );
+ }
+ catch ( Exception e ) {
+ throw new IllegalStateException( "Could not get property value", e );
+ }
+ return value;
+ }
+}
Property changes on: search/trunk/src/java/org/hibernate/search/util/ReflectionHelper.java
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -56,7 +56,7 @@
}
public void testSelectionOfShardingStrategy() throws Exception {
- IndexShardingStrategy shardingStrategy = getSearchFactory().getDocumentBuilder( Document.class )
+ IndexShardingStrategy shardingStrategy = getSearchFactory().getDocumentBuilderIndexedEntity( Document.class )
.getDirectoryProviderSelectionStrategy();
assertNotNull( shardingStrategy );
assertEquals( shardingStrategy.getClass(), UselessShardingStrategy.class );
Modified: search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -23,7 +23,7 @@
import org.hibernate.search.Environment;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.backend.AddLuceneWork;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
@@ -54,7 +54,7 @@
s.close();
//create the work queue to send
Document doc = new Document();
- Field field = new Field( DocumentBuilder.CLASS_FIELDNAME, ts.getClass().getName(), Field.Store.YES, Field.Index.NOT_ANALYZED );
+ Field field = new Field( DocumentBuilderIndexedEntity.CLASS_FIELDNAME, ts.getClass().getName(), Field.Store.YES, Field.Index.NOT_ANALYZED );
doc.add( field );
field = new Field("id", "1", Field.Store.YES, Field.Index.NOT_ANALYZED );
doc.add( field );
Modified: search/trunk/src/test/org/hibernate/search/test/worker/duplication/WorkDuplicationTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/worker/duplication/WorkDuplicationTest.java 2008-11-21 13:58:05 UTC (rev 15604)
+++ search/trunk/src/test/org/hibernate/search/test/worker/duplication/WorkDuplicationTest.java 2008-11-23 10:48:59 UTC (rev 15605)
@@ -14,12 +14,11 @@
import org.hibernate.Transaction;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
-import org.hibernate.search.SearchFactory;
import org.hibernate.search.backend.WorkType;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.AddLuceneWork;
import org.hibernate.search.backend.DeleteLuceneWork;
-import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
import org.hibernate.search.impl.SearchFactoryImpl;
import org.hibernate.search.reader.ReaderProvider;
import org.hibernate.search.store.DirectoryProvider;
@@ -104,7 +103,7 @@
public void testAddWorkGetReplacedByDeleteWork() throws Exception {
FullTextSession fullTextSession = org.hibernate.search.Search.getFullTextSession( openSession() );
SearchFactoryImpl searchFactory = ( SearchFactoryImpl ) fullTextSession.getSearchFactory();
- DocumentBuilder builder = searchFactory.getDocumentBuilder( SpecialPerson.class );
+ DocumentBuilderIndexedEntity builder = searchFactory.getDocumentBuilderIndexedEntity( SpecialPerson.class );
// create test entity
SpecialPerson person = new SpecialPerson();
16 years
Hibernate SVN: r15604 - in search/trunk/src/java/org/hibernate/search: jpa and 1 other directories.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2008-11-21 08:58:05 -0500 (Fri, 21 Nov 2008)
New Revision: 15604
Modified:
search/trunk/src/java/org/hibernate/search/FullTextSession.java
search/trunk/src/java/org/hibernate/search/jpa/FullTextEntityManager.java
search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java
Log:
Made sure the FulltextSession and FullTextEntityManager interfaces are consistent; updated javadoc
Modified: search/trunk/src/java/org/hibernate/search/FullTextSession.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/FullTextSession.java 2008-11-20 16:42:49 UTC (rev 15603)
+++ search/trunk/src/java/org/hibernate/search/FullTextSession.java 2008-11-21 13:58:05 UTC (rev 15604)
@@ -11,6 +11,7 @@
* @author Emmanuel Bernard
*/
public interface FullTextSession extends Session {
+
/**
* Create a fulltext query on top of a native Lucene query returning the matching objects
* of type <code>entities</code> and their respective subclasses.
@@ -20,6 +21,8 @@
* the specified types and their respective subtype. If no class is specified no type filtering will take place.
*
* @return A <code>FullTextQuery</code> wrapping around the native Lucene wuery.
+ *
+ * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
*/
FullTextQuery createFullTextQuery(org.apache.lucene.search.Query luceneQuery, Class<?>... entities);
@@ -47,7 +50,7 @@
* @param entityType The type of the entity to delete.
* @param id The id of the entity to delete.
*
- * @throws IllegalArgumentException if entityType is <code>null</codE> or not an @Indexed entity type.
+ * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
*/
public <T> void purge(Class<T> entityType, Serializable id);
@@ -56,13 +59,12 @@
*
* @param entityType The class of the entities to remove.
*
- * @throws IllegalArgumentException if entityType is <code>null</code> or not an @Indexed entity type.
+ * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
*/
public <T> void purgeAll(Class<T> entityType);
/**
- * flush full text changes to the index
- * Force Hibernate Search to apply all changes to the index no waiting for the batch limit
+ * Flush all index changes forcing Hibernate Search to apply all changes to the index not waiting for the batch limit.
*/
public void flushToIndexes();
}
Modified: search/trunk/src/java/org/hibernate/search/jpa/FullTextEntityManager.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/jpa/FullTextEntityManager.java 2008-11-20 16:42:49 UTC (rev 15603)
+++ search/trunk/src/java/org/hibernate/search/jpa/FullTextEntityManager.java 2008-11-21 13:58:05 UTC (rev 15604)
@@ -12,48 +12,60 @@
* @author Emmanuel Bernard
*/
public interface FullTextEntityManager extends EntityManager {
+
/**
- * Create a Query on top of a native Lucene Query returning the matching objects
+ * Create a fulltext query on top of a native Lucene query returning the matching objects
* of type <code>entities</code> and their respective subclasses.
- * If no entity is provided, no type filtering is done.
+ *
+ * @param luceneQuery The native Lucene query to be rn against the Lucene index.
+ * @param entities List of classes for type filtering. The query result will only return entities of
+ * the specified types and their respective subtype. If no class is specified no type filtering will take place.
+ *
+ * @return A <code>FullTextQuery</code> wrapping around the native Lucene wuery.
+ *
+ * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
*/
- FullTextQuery createFullTextQuery(org.apache.lucene.search.Query luceneQuery, Class... entities);
+ FullTextQuery createFullTextQuery(org.apache.lucene.search.Query luceneQuery, Class<?>... entities);
/**
* Force the (re)indexing of a given <b>managed</b> object.
* Indexation is batched per transaction: if a transaction is active, the operation
* will not affect the index at least until commit.
- *
+ *
+ * @param entity The entity to index - must not be <code>null</code>.
+ *
* @throws IllegalArgumentException if entity is null or not an @Indexed entity
*/
- void index(Object entity);
+ <T> void index(T entity);
/**
- * return the SearchFactory
+ * @return the <code>SearchFactory</code> instance.
*/
SearchFactory getSearchFactory();
+
/**
- * Remove a particular entity from a particular class of an index.
+ * Remove the entity with the type <code>entityType</code> and the identifier <code>id</code> from the index.
+ * If <code>id == null</code> all indexed entities of this type and its indexed subclasses are deleted. In this
+ * case this method behaves like {@link #purgeAll(Class)}.
*
- * @param entityType
- * @param id
+ * @param entityType The type of the entity to delete.
+ * @param id The id of the entity to delete.
*
- * @throws IllegalArgumentException if entityType is null or not an @Indexed entity type
+ * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
*/
- public void purge(Class entityType, Serializable id);
+ public <T> void purge(Class<T> entityType, Serializable id);
/**
- * Remove all entities from a particular class of an index.
+ * Remove all entities from of particular class and all its subclasses from the index.
*
- * @param entityType
+ * @param entityType The class of the entities to remove.
*
- * @throws IllegalArgumentException if entityType is null or not an @Indexed entity type
+ * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
*/
- public void purgeAll(Class entityType);
+ public <T> void purgeAll(Class<T> entityType);
/**
- * flush index change
- * Force Hibernate Search to apply all changes to the index no waiting for the batch limit
+ * Flush all index changes forcing Hibernate Search to apply all changes to the index not waiting for the batch limit.
*/
public void flushToIndexes();
Modified: search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java 2008-11-20 16:42:49 UTC (rev 15603)
+++ search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java 2008-11-21 13:58:05 UTC (rev 15604)
@@ -31,26 +31,34 @@
if ( ftSession == null ) {
Object delegate = em.getDelegate();
if ( delegate == null ) {
- throw new SearchException("Trying to use Hibernate Search without an Hibernate EntityManager (no delegate)");
+ throw new SearchException(
+ "Trying to use Hibernate Search without an Hibernate EntityManager (no delegate)"
+ );
}
else if ( Session.class.isAssignableFrom( delegate.getClass() ) ) {
- ftSession = Search.getFullTextSession( (Session) delegate );
+ ftSession = Search.getFullTextSession( ( Session ) delegate );
}
else if ( EntityManager.class.isAssignableFrom( delegate.getClass() ) ) {
//Some app servers wrap the EM twice
- delegate = ( (EntityManager) delegate).getDelegate();
+ delegate = ( ( EntityManager ) delegate ).getDelegate();
if ( delegate == null ) {
- throw new SearchException("Trying to use Hibernate Search without an Hibernate EntityManager (no delegate)");
+ throw new SearchException(
+ "Trying to use Hibernate Search without an Hibernate EntityManager (no delegate)"
+ );
}
else if ( Session.class.isAssignableFrom( delegate.getClass() ) ) {
- ftSession = Search.getFullTextSession( (Session) delegate );
+ ftSession = Search.getFullTextSession( ( Session ) delegate );
}
else {
- throw new SearchException("Trying to use Hibernate Search without an Hibernate EntityManager: " + delegate.getClass() );
+ throw new SearchException(
+ "Trying to use Hibernate Search without an Hibernate EntityManager: " + delegate.getClass()
+ );
}
}
else {
- throw new SearchException("Trying to use Hibernate Search without an Hibernate EntityManager: " + delegate.getClass() );
+ throw new SearchException(
+ "Trying to use Hibernate Search without an Hibernate EntityManager: " + delegate.getClass()
+ );
}
}
return ftSession;
@@ -61,7 +69,7 @@
return new FullTextQueryImpl( ftSession.createFullTextQuery( luceneQuery, entities ), ftSession );
}
- public void index(Object entity) {
+ public <T> void index(T entity) {
getFullTextSession().index( entity );
}
@@ -69,11 +77,11 @@
return getFullTextSession().getSearchFactory();
}
- public void purge(Class entityType, Serializable id) {
+ public <T> void purge(Class<T> entityType, Serializable id) {
getFullTextSession().purge( entityType, id );
}
- public void purgeAll(Class entityType) {
+ public <T> void purgeAll(Class<T> entityType) {
getFullTextSession().purgeAll( entityType );
}
@@ -81,7 +89,6 @@
getFullTextSession().flushToIndexes();
}
-
public void persist(Object entity) {
em.persist( entity );
}
16 years
Hibernate SVN: r15603 - search/trunk/src/java/org/hibernate/search/query.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2008-11-20 11:42:49 -0500 (Thu, 20 Nov 2008)
New Revision: 15603
Modified:
search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
search/trunk/src/java/org/hibernate/search/query/QueryHits.java
Log:
HSEARCH-302
Now QueryHits takes into acount pagination settings when retrieving TopDocs.
Modified: search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-20 14:17:21 UTC (rev 15602)
+++ search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-20 16:42:49 UTC (rev 15603)
@@ -154,7 +154,6 @@
queryHits, searchFactoryImplementor, indexProjection, idFieldNames, allowFieldSelectionInProjection
);
for ( int index = first; index <= max; index++ ) {
- //TODO use indexSearcher.getIndexReader().document( hits.id(index), FieldSelector(indexProjection) );
infos.add( extractor.extract( index ) );
}
Loader loader = getLoader( sess, searchFactoryImplementor );
@@ -342,7 +341,12 @@
private QueryHits getQueryHits(Searcher searcher) throws IOException {
org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
buildFilters();
- QueryHits queryHits = new QueryHits( searcher, query, filter, sort );
+ QueryHits queryHits;
+ if ( maxResults == null ) { // try to make sure that we get the right amount of top docs
+ queryHits = new QueryHits( searcher, query, filter, sort );
+ } else {
+ queryHits = new QueryHits( searcher, query, filter, sort, first() + maxResults );
+ }
resultSize = queryHits.totalHits;
return queryHits;
}
@@ -574,7 +578,6 @@
0;
}
-
/**
* Build the index searcher for this fulltext query.
*
Modified: search/trunk/src/java/org/hibernate/search/query/QueryHits.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/QueryHits.java 2008-11-20 14:17:21 UTC (rev 15602)
+++ search/trunk/src/java/org/hibernate/search/query/QueryHits.java 2008-11-20 16:42:49 UTC (rev 15603)
@@ -45,14 +45,20 @@
public final Sort sort;
public final int totalHits;
public TopDocs topDocs;
-
+
public QueryHits(Searcher searcher, org.apache.lucene.search.Query preparedQuery, Filter filter, Sort sort)
throws IOException {
+ this( searcher, preparedQuery, filter, sort, DEFAULT_TOP_DOC_RETRIEVAL_SIZE );
+ }
+
+ public QueryHits(Searcher searcher, org.apache.lucene.search.Query preparedQuery, Filter filter, Sort sort,
+ Integer n )
+ throws IOException {
this.preparedQuery = preparedQuery;
this.searcher = searcher;
this.filter = filter;
this.sort = sort;
- updateTopDocs( DEFAULT_TOP_DOC_RETRIEVAL_SIZE );
+ updateTopDocs( n );
totalHits = topDocs.totalHits;
}
16 years
Hibernate SVN: r15602 - in search/trunk/src: java/org/hibernate/search/query and 1 other directories.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2008-11-20 09:17:21 -0500 (Thu, 20 Nov 2008)
New Revision: 15602
Added:
search/trunk/src/test/org/hibernate/search/test/inheritance/Bird.java
search/trunk/src/test/org/hibernate/search/test/inheritance/Eagle.java
Modified:
search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java
search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
search/trunk/src/test/org/hibernate/search/test/inheritance/InheritanceTest.java
Log:
HSEARCH-160
- purging and searching on unindexed classes throws now an exception
- extented the tests
Modified: search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java 2008-11-20 13:09:44 UTC (rev 15601)
+++ search/trunk/src/java/org/hibernate/search/impl/FullTextSessionImpl.java 2008-11-20 14:17:21 UTC (rev 15602)
@@ -106,18 +106,15 @@
return;
}
- // accessing the document builders is not strictly necessary but a small optimization plus let's make sure the
- // client didn't mess something up.
SearchFactoryImplementor searchFactoryImplementor = getSearchFactoryImplementor();
Set<Class<?>> targetedClasses = searchFactoryImplementor.getIndexedTypesPolymorphic( new Class[] {entityType} );
+ if ( targetedClasses.isEmpty() ) {
+ String msg = entityType.getName() + " is not an indexed entity or a subclass of an indexed entity";
+ throw new IllegalArgumentException( msg );
+ }
for ( Class clazz : targetedClasses ) {
DocumentBuilder builder = searchFactoryImplementor.getDocumentBuilder( clazz );
- if ( builder == null ) {
- String msg = "Entity to index is not an @Indexed entity: " + clazz.getName();
- throw new IllegalArgumentException( msg );
- }
-
Work<T> work;
if ( id == null ) {
// purge the main entity
Modified: search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-11-20 13:09:44 UTC (rev 15601)
+++ search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-11-20 14:17:21 UTC (rev 15602)
@@ -534,17 +534,19 @@
}
Set<Class<?>> getIndexedClasses(Class<?>[] classes) {
- Set<Class<?>> classesSet = new HashSet<Class<?>>();
+ Set<Class<?>> idexedClasses = new HashSet<Class<?>>();
for ( Class clazz : classes ) {
Set<Class<?>> set = classToIndexedClass.get( clazz );
if ( set != null ) {
- classesSet.addAll( set );
+ // at this point we don't have to care about including indexed subclasses of a indexed class
+ // MultiClassesQueryLoader will take care of this later and optimise the queries
+ idexedClasses.addAll( set );
}
}
if ( log.isTraceEnabled() ) {
- log.trace( "Targeted indexed classes for {}: {}", Arrays.toString( classes ), classesSet );
+ log.trace( "Targeted indexed classes for {}: {}", Arrays.toString( classes ), idexedClasses );
}
- return classesSet;
+ return idexedClasses;
}
}
}
Modified: search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-20 13:09:44 UTC (rev 15601)
+++ search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-11-20 14:17:21 UTC (rev 15602)
@@ -104,6 +104,10 @@
super( query.toString(), null, session, parameterMetadata );
this.luceneQuery = query;
this.targetedEntities = getSearchFactoryImplementor().getIndexedTypesPolymorphic( classes );
+ if ( classes != null && classes.length > 0 && targetedEntities.size() == 0 ) {
+ String msg = "None of the specified entity types or any of their subclasses are indexed.";
+ throw new IllegalArgumentException( msg );
+ }
}
/**
Copied: search/trunk/src/test/org/hibernate/search/test/inheritance/Bird.java (from rev 15592, search/trunk/src/test/org/hibernate/search/test/inheritance/Fish.java)
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/inheritance/Bird.java (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/inheritance/Bird.java 2008-11-20 14:17:21 UTC (rev 15602)
@@ -0,0 +1,28 @@
+//$Id$
+package org.hibernate.search.test.inheritance;
+
+import javax.persistence.Entity;
+
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.Store;
+
+/**
+ * @author Hardy Ferentschik
+ */
+@Entity
+@Indexed
+public class Bird extends Animal {
+
+ private int numberOfEggs;
+
+ @Field(index = Index.UN_TOKENIZED, store = Store.YES)
+ public int getNumberOfEggs() {
+ return numberOfEggs;
+ }
+
+ public void setNumberOfEggs(int numberOfEggs) {
+ this.numberOfEggs = numberOfEggs;
+ }
+}
\ No newline at end of file
Added: search/trunk/src/test/org/hibernate/search/test/inheritance/Eagle.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/inheritance/Eagle.java (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/inheritance/Eagle.java 2008-11-20 14:17:21 UTC (rev 15602)
@@ -0,0 +1,33 @@
+//$Id: Fish.java 15592 2008-11-19 14:17:48Z hardy.ferentschik $
+package org.hibernate.search.test.inheritance;
+
+import javax.persistence.Entity;
+
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.Store;
+
+/**
+ * @author Hardy Ferentschik
+ */
+@Entity
+@Indexed
+public class Eagle extends Bird {
+
+ private WingType wingYype;
+
+ @Field(index = Index.UN_TOKENIZED, store = Store.YES)
+ public WingType getWingYype() {
+ return wingYype;
+ }
+
+ public void setWingYype(WingType wingYype) {
+ this.wingYype = wingYype;
+ }
+
+ public enum WingType {
+ BROAD,
+ LONG
+ }
+}
\ No newline at end of file
Modified: search/trunk/src/test/org/hibernate/search/test/inheritance/InheritanceTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/inheritance/InheritanceTest.java 2008-11-20 13:09:44 UTC (rev 15601)
+++ search/trunk/src/test/org/hibernate/search/test/inheritance/InheritanceTest.java 2008-11-20 14:17:21 UTC (rev 15602)
@@ -9,10 +9,13 @@
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.RangeQuery;
+import org.apache.lucene.search.TermQuery;
+import org.slf4j.Logger;
import org.hibernate.Transaction;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
+import org.hibernate.search.util.LoggerFactory;
import org.hibernate.search.test.SearchTestCase;
/**
@@ -20,10 +23,38 @@
*/
public class InheritanceTest extends SearchTestCase {
+ private static final Logger log = LoggerFactory.make();
+
protected void setUp() throws Exception {
super.setUp();
}
+ public void testSearchUnindexClass() throws Exception {
+ createTestData();
+
+ QueryParser parser = new QueryParser( "name", new StopAnalyzer() );
+ Query query = parser.parse( "Elephant" );
+
+ FullTextSession s = Search.getFullTextSession( openSession() );
+ Transaction tx = s.beginTransaction();
+ try {
+ org.hibernate.Query hibQuery = s.createFullTextQuery( query, String.class );
+ hibQuery.list();
+ tx.commit();
+ fail();
+ }
+ catch ( IllegalArgumentException iae ) {
+ log.debug( "success" );
+ }
+
+ tx = s.beginTransaction();
+ org.hibernate.Query hibQuery = s.createFullTextQuery( query, Mammal.class );
+ assertItsTheElephant( hibQuery.list() );
+ tx.commit();
+
+ s.close();
+ }
+
public void testInheritance() throws Exception {
createTestData();
@@ -35,6 +66,10 @@
org.hibernate.Query hibQuery = s.createFullTextQuery( query, Mammal.class );
assertItsTheElephant( hibQuery.list() );
+ query = parser.parse( "Elephant" );
+ hibQuery = s.createFullTextQuery( query);
+ assertItsTheElephant( hibQuery.list() );
+
query = parser.parse( "hasSweatGlands:false" );
hibQuery = s.createFullTextQuery( query, Animal.class, Mammal.class );
assertItsTheElephant( hibQuery.list() );
@@ -81,19 +116,51 @@
hibQuery = s.createFullTextQuery( query, Serializable.class );
assertItsTheElephant( hibQuery.list() );
- hibQuery = s.createFullTextQuery( query, Mammal.class, Animal.class, Being.class, Object.class, Serializable.class );
+ hibQuery = s.createFullTextQuery(
+ query, Mammal.class, Animal.class, Being.class, Object.class, Serializable.class
+ );
assertItsTheElephant( hibQuery.list() );
tx.commit();
s.close();
}
- private void assertItsTheElephant(List result) {
+ public void testSubclassInclusion() throws Exception {
+ createTestData();
+
+ FullTextSession s = Search.getFullTextSession( openSession() );
+ Transaction tx = s.beginTransaction();
+
+ Query query = new TermQuery( new Term( "numberOfEggs", "2" ) );
+ org.hibernate.Query hibQuery = s.createFullTextQuery( query, Eagle.class );
+ List result = hibQuery.list();
assertNotNull( result );
- assertEquals( "Wrong number of results", 1, result.size() );
- assertTrue( "Wrong result type", result.get( 0 ) instanceof Mammal );
- Mammal mammal = ( Mammal ) result.get( 0 );
- assertEquals( "Wrong animal name", "Elephant", mammal.getName() );
+ assertEquals( "Wrong number of hits. There should be two birds.", 1, result.size() );
+
+ query = new TermQuery( new Term( "numberOfEggs", "2" ) );
+ hibQuery = s.createFullTextQuery( query, Bird.class );
+ result = hibQuery.list();
+ assertNotNull( result );
+ assertEquals( "Wrong number of hits. There should be two birds.", 2, result.size() );
+
+ query = new TermQuery( new Term( "numberOfEggs", "2" ) );
+ hibQuery = s.createFullTextQuery( query, Mammal.class );
+ result = hibQuery.list();
+ assertNotNull( result );
+ assertEquals( "Wrong number of hits. There should be two birds.", 0, result.size() );
+
+ try {
+ query = new TermQuery( new Term( "numberOfEggs", "2" ) );
+ hibQuery = s.createFullTextQuery( query, String.class );
+ hibQuery.list();
+ fail();
+ }
+ catch ( IllegalArgumentException iae ) {
+ log.debug( "success" );
+ }
+
+ tx.commit();
+ s.close();
}
/**
@@ -104,27 +171,71 @@
public void testPurgeIndex() throws Exception {
createTestData();
FullTextSession s = Search.getFullTextSession( openSession() );
+
Transaction tx = s.beginTransaction();
- QueryParser parser = new QueryParser( "name", new StopAnalyzer() );
+ assertNumberOfAnimals( s, 5 );
+ tx.commit();
- Query query = parser.parse( "Elephant OR White Pointer OR Chimpanzee" );
- List result = s.createFullTextQuery( query, Animal.class ).list();
- assertNotNull( result );
- assertEquals( "Wrong number of hits. There should be one elephant and one shark.", 3, result.size() );
+ tx = s.beginTransaction();
+ s.purgeAll( Serializable.class );
+ tx.commit();
- s.purgeAll( Animal.class );
+ tx = s.beginTransaction();
+ assertNumberOfAnimals( s, 3 );
tx.commit();
+
tx = s.beginTransaction();
- result = s.createFullTextQuery( query, Animal.class ).list();
- assertNotNull( result );
- assertEquals(
- "Wrong number of hits. Purging the Animal class should also purge the Mammals", 0, result.size()
- );
+ s.purgeAll( Bird.class );
+ tx.commit();
+ tx = s.beginTransaction();
+ assertNumberOfAnimals( s, 1 );
tx.commit();
+
+ tx = s.beginTransaction();
+ s.purgeAll( Object.class );
+ tx.commit();
+
+ tx = s.beginTransaction();
+ assertNumberOfAnimals( s, 0 );
+ tx.commit();
+
s.close();
}
+ /**
+ * Tests that purging the an uninexed class triggers an exception.
+ *
+ * @throws Exception in case the test fails.
+ */
+ public void testPurgeUnIndexClass() throws Exception {
+ createTestData();
+ FullTextSession s = Search.getFullTextSession( openSession() );
+
+ Transaction tx = s.beginTransaction();
+ assertNumberOfAnimals( s, 5 );
+ tx.commit();
+
+ tx = s.beginTransaction();
+ try {
+ s.purgeAll( String.class );
+ tx.commit();
+ fail();
+ }
+ catch ( IllegalArgumentException iae ) {
+ log.debug( "Success" );
+ }
+ s.close();
+ }
+
+ private void assertNumberOfAnimals(FullTextSession s, int count) throws Exception {
+ QueryParser parser = new QueryParser( "name", new StopAnalyzer() );
+ Query query = parser.parse( "Elephant OR White Pointer OR Chimpanzee OR Dove or Eagle" );
+ List result = s.createFullTextQuery( query, Animal.class ).list();
+ assertNotNull( result );
+ assertEquals( "Wrong number of hits. There should be one elephant and one shark.", count, result.size() );
+ }
+
private void createTestData() {
FullTextSession s = Search.getFullTextSession( openSession() );
Transaction tx = s.beginTransaction();
@@ -147,15 +258,36 @@
chimp.setWeight( 50 );
s.save( chimp );
+ Bird dove = new Bird();
+ dove.setName( "Dove" );
+ dove.setNumberOfEggs( 2 );
+ s.save( dove );
+
+ Eagle eagle = new Eagle();
+ eagle.setName( "Bald Eagle" );
+ eagle.setNumberOfEggs( 2 );
+ eagle.setWingYype( Eagle.WingType.BROAD );
+ s.save( eagle );
+
tx.commit();
s.clear();
}
+ private void assertItsTheElephant(List result) {
+ assertNotNull( result );
+ assertEquals( "Wrong number of results", 1, result.size() );
+ assertTrue( "Wrong result type", result.get( 0 ) instanceof Mammal );
+ Mammal mammal = ( Mammal ) result.get( 0 );
+ assertEquals( "Wrong animal name", "Elephant", mammal.getName() );
+ }
+
protected Class[] getMappings() {
return new Class[] {
Animal.class,
Mammal.class,
- Fish.class
+ Fish.class,
+ Bird.class,
+ Eagle.class
};
}
}
16 years
Hibernate SVN: r15601 - in core/trunk/envers/src/test: java/org/hibernate/envers/test/performance and 2 other directories.
by hibernate-commits@lists.jboss.org
Author: adamw
Date: 2008-11-20 08:09:44 -0500 (Thu, 20 Nov 2008)
New Revision: 15601
Added:
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/ComplexInsertPerformance.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsOneTransactionPerformance.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity1.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity2.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/RootEntity.java
Modified:
core/trunk/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/AbstractPerformanceTest.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsPerformance.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/UpdatesPerformance.java
core/trunk/envers/src/test/resources/hibernate.test.cfg.xml
Log:
Performance tests
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java 2008-11-19 17:38:55 UTC (rev 15600)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -29,10 +29,13 @@
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
+import org.hibernate.envers.event.AuditEventListener;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterClass;
import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.event.*;
/**
* @author Adam Warski (adam at warski dot org)
@@ -42,22 +45,50 @@
private EntityManager entityManager;
private AuditReader auditReader;
private Ejb3Configuration cfg;
+ private boolean audited;
public abstract void configure(Ejb3Configuration cfg);
- @BeforeMethod
- public void newEntityManager() {
+ protected void initListeners() {
+ AuditEventListener listener = new AuditEventListener();
+ cfg.getEventListeners().setPostInsertEventListeners(new PostInsertEventListener[] { listener });
+ cfg.getEventListeners().setPostUpdateEventListeners(new PostUpdateEventListener[] { listener });
+ cfg.getEventListeners().setPostDeleteEventListeners(new PostDeleteEventListener[] { listener });
+ cfg.getEventListeners().setPreCollectionUpdateEventListeners(new PreCollectionUpdateEventListener[] { listener });
+ cfg.getEventListeners().setPreCollectionRemoveEventListeners(new PreCollectionRemoveEventListener[] { listener });
+ cfg.getEventListeners().setPostCollectionRecreateEventListeners(new PostCollectionRecreateEventListener[] { listener });
+ }
+
+ private void closeEntityManager() {
if (entityManager != null) {
entityManager.close();
+ entityManager = null;
}
+ }
+
+ @BeforeMethod
+ public void newEntityManager() {
+ closeEntityManager();
entityManager = emf.createEntityManager();
- auditReader = AuditReaderFactory.get(entityManager);
+
+ if (audited) {
+ auditReader = AuditReaderFactory.get(entityManager);
+ }
}
@BeforeClass
public void init() throws IOException {
+ init(true);
+ }
+
+ protected void init(boolean audited) throws IOException {
+ this.audited = audited;
+
cfg = new Ejb3Configuration();
+ if (audited) {
+ initListeners();
+ }
cfg.configure("hibernate.test.cfg.xml");
configure(cfg);
emf = cfg.buildEntityManagerFactory();
@@ -65,6 +96,12 @@
newEntityManager();
}
+ @AfterClass
+ public void close() {
+ closeEntityManager();
+ emf.close();
+ }
+
public EntityManager getEntityManager() {
return entityManager;
}
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/AbstractPerformanceTest.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/AbstractPerformanceTest.java 2008-11-19 17:38:55 UTC (rev 15600)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/AbstractPerformanceTest.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -24,8 +24,11 @@
package org.hibernate.envers.test.performance;
import org.hibernate.envers.test.AbstractEntityTest;
-import org.hibernate.envers.tools.Pair;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
/**
* @author Adam Warski (adam at warski dot org)
*/
@@ -34,49 +37,72 @@
return (milliseconds/1000) + "." + (milliseconds%1000);
}
- protected long measureTime(Runnable r) {
- long start = System.currentTimeMillis();
- r.run();
- return System.currentTimeMillis() - start;
+ protected abstract void doTest();
+
+ private void printResults(long unaudited, long audited) {
+ System.out.println("Unaudited: " + getSecondsString(unaudited));
+ System.out.println(" Audited: " + getSecondsString(audited));
+ System.out.println(" Delta: " + getSecondsString(audited-unaudited));
+ System.out.println(" Factor: " + (double)audited/unaudited);
}
- protected abstract Pair<Long, Long> doTest();
+ private long startTime;
+ private long runTotal;
- protected abstract String getName();
+ protected void start() {
+ startTime = System.currentTimeMillis();
+ }
- private long totalUnversioned;
- private long totalVersioned;
+ protected void stop() {
+ long stopTime = System.currentTimeMillis();
+ runTotal += stopTime - startTime;
+ }
- private void printResults(long unversioned, long versioned) {
- System.out.println("Unversioned: " + getSecondsString(unversioned));
- System.out.println(" Versioned: " + getSecondsString(versioned));
- System.out.println(" Delta: " + getSecondsString(versioned-unversioned));
- System.out.println(" Factor: " + (double)versioned/unversioned);
+ protected void reset() {
+ runTotal = 0;
}
- private void test(boolean count) {
- Pair<Long, Long> result = doTest();
- long unversioned = result.getFirst();
- long versioned = result.getSecond();
+ public long run(int numberOfRuns, List<Long> results) {
+ long total = 0;
+ for (int i=0; i<=numberOfRuns; i++) {
+ System.out.println();
+ System.out.println("RUN " + i);
+ reset();
+ doTest();
+ results.add(runTotal);
+ total += runTotal;
- totalUnversioned += unversioned;
- totalVersioned += versioned;
+ newEntityManager();
- printResults(unversioned, versioned);
+ /*System.gc();
+ System.gc();
+ System.gc();
+ System.out.println(Runtime.getRuntime().freeMemory() + ", " + Runtime.getRuntime().totalMemory() + ", "
+ + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()));*/
+ }
+
+ return total;
}
- public void run(int numberOfRuns) {
+ public void test(int numberOfRuns) throws IOException {
+ List<Long> unauditedRuns = new ArrayList<Long>();
+ List<Long> auditedRuns = new ArrayList<Long>();
+
+ init(true);
+ long audited = run(numberOfRuns, auditedRuns);
+ close();
+
+ init(false);
+ long unaudited = run(numberOfRuns, unauditedRuns);
+ close();
+
for (int i=0; i<=numberOfRuns; i++) {
- System.out.println("");
- System.out.println(getName() + " TEST, RUN " + i);
- test(i != 0);
+ System.out.println("RUN " + i);
+ printResults(unauditedRuns.get(i), auditedRuns.get(i));
+ System.out.println();
}
- totalUnversioned /= numberOfRuns;
- totalVersioned /= numberOfRuns;
-
- System.out.println("");
- System.out.println(getName() + " TEST, AVERAGE");
- printResults(totalUnversioned, totalVersioned);
+ System.out.println("TOTAL");
+ printResults(unaudited, audited);
}
}
Copied: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/ComplexInsertPerformance.java (from rev 15586, core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsPerformance.java)
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/ComplexInsertPerformance.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/ComplexInsertPerformance.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -0,0 +1,105 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * 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, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY 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
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+
+package org.hibernate.envers.test.performance;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashSet;
+import javax.persistence.EntityManager;
+
+import org.hibernate.envers.test.performance.complex.RootEntity;
+import org.hibernate.envers.test.performance.complex.ChildEntity2;
+import org.hibernate.envers.test.performance.complex.ChildEntity1;
+
+import org.hibernate.ejb.Ejb3Configuration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ComplexInsertPerformance extends AbstractPerformanceTest {
+ public void configure(Ejb3Configuration cfg) {
+ cfg.addAnnotatedClass(RootEntity.class);
+ cfg.addAnnotatedClass(ChildEntity1.class);
+ cfg.addAnnotatedClass(ChildEntity2.class);
+ }
+
+ private final static int NUMBER_INSERTS = 100;
+
+ private long idCounter = 0;
+
+ private ChildEntity2 createChildEntity2() {
+ ChildEntity2 ce = new ChildEntity2();
+ ce.setId(idCounter++);
+ ce.setNumber(12345678);
+ ce.setData("some data, not really meaningful");
+ ce.setStrings(new HashSet<String>());
+ ce.getStrings().add("aaa");
+ ce.getStrings().add("bbb");
+ ce.getStrings().add("ccc");
+
+ return ce;
+ }
+
+ private ChildEntity1 createChildEntity1() {
+ ChildEntity1 ce = new ChildEntity1();
+ ce.setId(idCounter++);
+ ce.setData1("xxx");
+ ce.setData2("yyy");
+ ce.setChild1(createChildEntity2());
+ ce.setChild2(createChildEntity2());
+
+ return ce;
+ }
+
+ protected void doTest() {
+ for (int i=0; i<NUMBER_INSERTS; i++) {
+ newEntityManager();
+ EntityManager entityManager = getEntityManager();
+
+ entityManager.getTransaction().begin();
+
+ RootEntity re = new RootEntity();
+ re.setId(idCounter++);
+ re.setData1("data1");
+ re.setData2("data2");
+ re.setDate1(new Date());
+ re.setNumber1(123);
+ re.setNumber2(456);
+ re.setChild1(createChildEntity1());
+ re.setChild2(createChildEntity1());
+ re.setChild3(createChildEntity1());
+
+ start();
+ entityManager.persist(re);
+ entityManager.getTransaction().commit();
+ stop();
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ ComplexInsertPerformance insertsPerformance = new ComplexInsertPerformance();
+ insertsPerformance.test(3);
+ }
+}
Property changes on: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/ComplexInsertPerformance.java
___________________________________________________________________
Name: svn:mergeinfo
+
Copied: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsOneTransactionPerformance.java (from rev 15586, core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsPerformance.java)
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsOneTransactionPerformance.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsOneTransactionPerformance.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -0,0 +1,59 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * 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, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY 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
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+
+package org.hibernate.envers.test.performance;
+
+import java.io.IOException;
+import javax.persistence.EntityManager;
+
+import org.hibernate.envers.test.entities.StrTestEntity;
+
+import org.hibernate.ejb.Ejb3Configuration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class InsertsOneTransactionPerformance extends AbstractPerformanceTest {
+ public void configure(Ejb3Configuration cfg) {
+ cfg.addAnnotatedClass(StrTestEntity.class);
+ }
+
+ private final static int NUMBER_INSERTS = 5000;
+
+ protected void doTest() {
+ EntityManager entityManager = getEntityManager();
+ entityManager.getTransaction().begin();
+ start();
+ for (int i=0; i<NUMBER_INSERTS; i++) {
+ entityManager.persist(new StrTestEntity("x" + i));
+ }
+ entityManager.getTransaction().commit();
+ stop();
+ }
+
+ public static void main(String[] args) throws IOException {
+ InsertsOneTransactionPerformance insertsOneTransactionPerformance = new InsertsOneTransactionPerformance();
+ insertsOneTransactionPerformance.test(3);
+ }
+}
\ No newline at end of file
Property changes on: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsOneTransactionPerformance.java
___________________________________________________________________
Name: svn:mergeinfo
+
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsPerformance.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsPerformance.java 2008-11-19 17:38:55 UTC (rev 15600)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/InsertsPerformance.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -27,8 +27,6 @@
import javax.persistence.EntityManager;
import org.hibernate.envers.test.entities.StrTestEntity;
-import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
-import org.hibernate.envers.tools.Pair;
import org.hibernate.ejb.Ejb3Configuration;
@@ -38,43 +36,25 @@
public class InsertsPerformance extends AbstractPerformanceTest {
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(StrTestEntity.class);
- cfg.addAnnotatedClass(UnversionedStrTestEntity.class);
}
- private final static int NUMBER_INSERTS = 1000;
-
- private void insertUnversioned() {
- EntityManager entityManager = getEntityManager();
- for (int i=0; i<NUMBER_INSERTS; i++) {
- entityManager.getTransaction().begin();
- entityManager.persist(new UnversionedStrTestEntity("x" + i));
- entityManager.getTransaction().commit();
- }
- }
+ private final static int NUMBER_INSERTS = 500;
- private void insertVersioned() {
- EntityManager entityManager = getEntityManager();
+ protected void doTest() {
for (int i=0; i<NUMBER_INSERTS; i++) {
+ newEntityManager();
+ EntityManager entityManager = getEntityManager();
+
entityManager.getTransaction().begin();
+ start();
entityManager.persist(new StrTestEntity("x" + i));
entityManager.getTransaction().commit();
+ stop();
}
}
- protected Pair<Long, Long> doTest() {
- long unversioned = measureTime(new Runnable() { public void run() { insertUnversioned(); } });
- long versioned = measureTime(new Runnable() { public void run() { insertVersioned(); } });
-
- return Pair.make(unversioned, versioned);
- }
-
- protected String getName() {
- return "INSERTS";
- }
-
public static void main(String[] args) throws IOException {
InsertsPerformance insertsPerformance = new InsertsPerformance();
- insertsPerformance.init();
- insertsPerformance.run(10);
+ insertsPerformance.test(3);
}
}
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/UpdatesPerformance.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/UpdatesPerformance.java 2008-11-19 17:38:55 UTC (rev 15600)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/UpdatesPerformance.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -30,9 +30,6 @@
import javax.persistence.EntityManager;
import org.hibernate.envers.test.entities.StrTestEntity;
-import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
-import org.hibernate.envers.tools.Pair;
-
import org.hibernate.ejb.Ejb3Configuration;
/**
@@ -41,71 +38,46 @@
public class UpdatesPerformance extends AbstractPerformanceTest {
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(StrTestEntity.class);
- cfg.addAnnotatedClass(UnversionedStrTestEntity.class);
}
- private final static int NUMBER_UPDATES = 1000;
+ private final static int NUMBER_UPDATES = 500;
private final static int NUMBER_ENTITIES = 10;
private Random random = new Random();
- private List<Integer> unversioned_ids = new ArrayList<Integer>();
- private List<Integer> versioned_ids = new ArrayList<Integer>();
+ private List<Integer> ids = new ArrayList<Integer>();
- public void setup() {
+ private void setup() {
EntityManager entityManager = getEntityManager();
entityManager.getTransaction().begin();
- for (int i=0; i<NUMBER_ENTITIES; i++) {
- UnversionedStrTestEntity testEntity = new UnversionedStrTestEntity("x" + i);
- entityManager.persist(testEntity);
- unversioned_ids.add(testEntity.getId());
- }
for (int i=0; i<NUMBER_ENTITIES; i++) {
StrTestEntity testEntity = new StrTestEntity("x" + i);
entityManager.persist(testEntity);
- versioned_ids.add(testEntity.getId());
+ ids.add(testEntity.getId());
}
entityManager.getTransaction().commit();
}
- private void updateUnversioned() {
- EntityManager entityManager = getEntityManager();
- for (int i=0; i<NUMBER_UPDATES; i++) {
- entityManager.getTransaction().begin();
- Integer id = unversioned_ids.get(random.nextInt(NUMBER_ENTITIES));
- UnversionedStrTestEntity testEntity = entityManager.find(UnversionedStrTestEntity.class, id);
- testEntity.setStr("z" + i);
- entityManager.getTransaction().commit();
- }
- }
+ protected void doTest() {
+ setup();
- private void updateVersioned() {
- EntityManager entityManager = getEntityManager();
for (int i=0; i<NUMBER_UPDATES; i++) {
+ newEntityManager();
+ EntityManager entityManager = getEntityManager();
+
entityManager.getTransaction().begin();
- Integer id = versioned_ids.get(random.nextInt(NUMBER_ENTITIES));
+ Integer id = ids.get(random.nextInt(NUMBER_ENTITIES));
+ start();
StrTestEntity testEntity = entityManager.find(StrTestEntity.class, id);
testEntity.setStr("z" + i);
entityManager.getTransaction().commit();
+ stop();
}
}
- protected Pair<Long, Long> doTest() {
- long unversioned = measureTime(new Runnable() { public void run() { updateUnversioned(); } });
- long versioned = measureTime(new Runnable() { public void run() { updateVersioned(); } });
-
- return Pair.make(unversioned, versioned);
- }
-
- protected String getName() {
- return "UPDATES";
- }
-
public static void main(String[] args) throws IOException {
UpdatesPerformance updatesPerformance = new UpdatesPerformance();
- updatesPerformance.init();
- updatesPerformance.setup();
- updatesPerformance.run(10);
+ updatesPerformance.test(3);
}
}
\ No newline at end of file
Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity1.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity1.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity1.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -0,0 +1,113 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * 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, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY 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
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+
+package org.hibernate.envers.test.performance.complex;
+
+import org.hibernate.envers.Audited;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.CascadeType;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class ChildEntity1 {
+ @Id
+ private Long id;
+
+ @ManyToOne(cascade = CascadeType.ALL)
+ private ChildEntity2 child1;
+
+ @ManyToOne(cascade = CascadeType.ALL)
+ private ChildEntity2 child2;
+
+ private String data1;
+
+ private String data2;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public ChildEntity2 getChild1() {
+ return child1;
+ }
+
+ public void setChild1(ChildEntity2 child1) {
+ this.child1 = child1;
+ }
+
+ public ChildEntity2 getChild2() {
+ return child2;
+ }
+
+ public void setChild2(ChildEntity2 child2) {
+ this.child2 = child2;
+ }
+
+ public String getData1() {
+ return data1;
+ }
+
+ public void setData1(String data1) {
+ this.data1 = data1;
+ }
+
+ public String getData2() {
+ return data2;
+ }
+
+ public void setData2(String data2) {
+ this.data2 = data2;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ChildEntity1)) return false;
+
+ ChildEntity1 that = (ChildEntity1) o;
+
+ if (data1 != null ? !data1.equals(that.data1) : that.data1 != null) return false;
+ if (data2 != null ? !data2.equals(that.data2) : that.data2 != null) return false;
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data1 != null ? data1.hashCode() : 0);
+ result = 31 * result + (data2 != null ? data2.hashCode() : 0);
+ return result;
+ }
+}
Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity2.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity2.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/ChildEntity2.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -0,0 +1,102 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * 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, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY 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
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+
+package org.hibernate.envers.test.performance.complex;
+
+import org.hibernate.envers.Audited;
+import org.hibernate.annotations.CollectionOfElements;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class ChildEntity2 {
+ @Id
+ private Long id;
+
+ private String data;
+
+ private Integer number;
+
+ @CollectionOfElements
+ private Set<String> strings;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public Integer getNumber() {
+ return number;
+ }
+
+ public void setNumber(Integer number) {
+ this.number = number;
+ }
+
+ public Set<String> getStrings() {
+ return strings;
+ }
+
+ public void setStrings(Set<String> strings) {
+ this.strings = strings;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ChildEntity2)) return false;
+
+ ChildEntity2 that = (ChildEntity2) o;
+
+ if (data != null ? !data.equals(that.data) : that.data != null) return false;
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+ if (number != null ? !number.equals(that.number) : that.number != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data != null ? data.hashCode() : 0);
+ result = 31 * result + (number != null ? number.hashCode() : 0);
+ return result;
+ }
+}
\ No newline at end of file
Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/RootEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/RootEntity.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/performance/complex/RootEntity.java 2008-11-20 13:09:44 UTC (rev 15601)
@@ -0,0 +1,159 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * 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, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY 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
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+
+package org.hibernate.envers.test.performance.complex;
+
+import org.hibernate.envers.Audited;
+
+import javax.persistence.*;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class RootEntity {
+ @Id
+ private Long id;
+
+ private String data1;
+
+ private String data2;
+
+ private Integer number1;
+
+ private Integer number2;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date date1;
+
+ @ManyToOne(cascade = CascadeType.ALL)
+ private ChildEntity1 child1;
+
+ @ManyToOne(cascade = CascadeType.ALL)
+ private ChildEntity1 child2;
+
+ @ManyToOne(cascade = CascadeType.ALL)
+ private ChildEntity1 child3;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getData1() {
+ return data1;
+ }
+
+ public void setData1(String data1) {
+ this.data1 = data1;
+ }
+
+ public String getData2() {
+ return data2;
+ }
+
+ public void setData2(String data2) {
+ this.data2 = data2;
+ }
+
+ public Integer getNumber1() {
+ return number1;
+ }
+
+ public void setNumber1(Integer number1) {
+ this.number1 = number1;
+ }
+
+ public Integer getNumber2() {
+ return number2;
+ }
+
+ public void setNumber2(Integer number2) {
+ this.number2 = number2;
+ }
+
+ public Date getDate1() {
+ return date1;
+ }
+
+ public void setDate1(Date date1) {
+ this.date1 = date1;
+ }
+
+ public ChildEntity1 getChild1() {
+ return child1;
+ }
+
+ public void setChild1(ChildEntity1 child1) {
+ this.child1 = child1;
+ }
+
+ public ChildEntity1 getChild2() {
+ return child2;
+ }
+
+ public void setChild2(ChildEntity1 child2) {
+ this.child2 = child2;
+ }
+
+ public ChildEntity1 getChild3() {
+ return child3;
+ }
+
+ public void setChild3(ChildEntity1 child3) {
+ this.child3 = child3;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RootEntity)) return false;
+
+ RootEntity that = (RootEntity) o;
+
+ if (data1 != null ? !data1.equals(that.data1) : that.data1 != null) return false;
+ if (data2 != null ? !data2.equals(that.data2) : that.data2 != null) return false;
+ if (date1 != null ? !date1.equals(that.date1) : that.date1 != null) return false;
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+ if (number1 != null ? !number1.equals(that.number1) : that.number1 != null) return false;
+ if (number2 != null ? !number2.equals(that.number2) : that.number2 != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data1 != null ? data1.hashCode() : 0);
+ result = 31 * result + (data2 != null ? data2.hashCode() : 0);
+ result = 31 * result + (number1 != null ? number1.hashCode() : 0);
+ result = 31 * result + (number2 != null ? number2.hashCode() : 0);
+ result = 31 * result + (date1 != null ? date1.hashCode() : 0);
+ return result;
+ }
+}
Modified: core/trunk/envers/src/test/resources/hibernate.test.cfg.xml
===================================================================
--- core/trunk/envers/src/test/resources/hibernate.test.cfg.xml 2008-11-19 17:38:55 UTC (rev 15600)
+++ core/trunk/envers/src/test/resources/hibernate.test.cfg.xml 2008-11-20 13:09:44 UTC (rev 15601)
@@ -17,13 +17,15 @@
<property name="connection.username">sa</property>
<property name="connection.password"></property>
- <!--<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <!--<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="connection.url">jdbc:mysql:///hibernate_tests?useUnicode=true&characterEncoding=UTF-8</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">root</property>
- <property name="connection.password"></property>-->
+ <property name="connection.password"></property>
- <event type="post-insert">
+ <property name="hibernate.jdbc.batch_size">100</property>-->
+
+ <!--<event type="post-insert">
<listener class="org.hibernate.envers.event.AuditEventListener" />
</event>
<event type="post-update">
@@ -40,6 +42,6 @@
</event>
<event type="post-collection-recreate">
<listener class="org.hibernate.envers.event.AuditEventListener" />
- </event>
+ </event>-->
</session-factory>
</hibernate-configuration>
\ No newline at end of file
16 years
Hibernate SVN: r15600 - in validator/trunk: hibernate-validator/src/main/java/org/hibernate/validation/impl and 3 other directories.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2008-11-19 12:38:55 -0500 (Wed, 19 Nov 2008)
New Revision: 15600
Added:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImplementor.java
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaDataProviderImpl.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorImplTest.java
validator/trunk/validation-api/src/main/java/javax/validation/Validator.java
validator/trunk/validation-api/src/main/java/javax/validation/ValidatorFactory.java
Log:
BVAL-77 Move from Validator<T> to Validator and <T> at the method level
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaDataProviderImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaDataProviderImpl.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaDataProviderImpl.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -50,6 +50,7 @@
* instantiate an instance of this class and delegate the metadata extraction to it.
*
* @author Hardy Ferentschik
+ * FIXME create an interface for MetadataProvider
*/
public class MetaDataProviderImpl<T> implements MetaDataProvider<T> {
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -70,7 +70,7 @@
/**
* Stack for keep track of the currently validated object.
*/
- private Stack<Object> validatedobjectStack = new Stack<Object>();
+ private Stack<ValidatedBean> validatedobjectStack = new Stack<ValidatedBean>();
public ValidationContext(T object) {
@@ -79,18 +79,22 @@
public ValidationContext(T rootBean, Object object) {
this.rootBean = rootBean;
- validatedobjectStack.push( object );
+ validatedobjectStack.push( new ValidatedBean(object) );
processedObjects = new HashMap<String, IdentitySet>();
propertyPath = "";
failingConstraintViolations = new ArrayList<ConstraintViolationImpl<T>>();
}
public Object peekValidatedObject() {
- return validatedobjectStack.peek();
+ return validatedobjectStack.peek().bean;
}
+ public Class<?> peekValidatedObjectType() {
+ return validatedobjectStack.peek().beanType;
+ }
+
public void pushValidatedObject(Object validatedObject) {
- validatedobjectStack.push( validatedObject );
+ validatedobjectStack.push( new ValidatedBean(validatedObject) );
}
public void popValidatedObject() {
@@ -111,11 +115,11 @@
public void markProcessedForCurrentGroup() {
if ( processedObjects.containsKey( currentGroup ) ) {
- processedObjects.get( currentGroup ).add( validatedobjectStack.peek() );
+ processedObjects.get( currentGroup ).add( validatedobjectStack.peek().bean );
}
else {
IdentitySet set = new IdentitySet();
- set.add( validatedobjectStack.peek() );
+ set.add( validatedobjectStack.peek().bean );
processedObjects.put( currentGroup, set );
}
}
@@ -181,4 +185,21 @@
public boolean needsValidation(Set<String> groups) {
return groups.contains( currentGroup );
}
+
+ //TODO caching the object class is useful?
+ private static class ValidatedBean {
+
+ final Object bean;
+ final Class<?> beanType;
+
+ private ValidatedBean(Object bean) {
+ this.bean = bean;
+ if (bean == null) {
+ this.beanType = null;
+ }
+ else {
+ this.beanType = bean.getClass();
+ }
+ }
+ }
}
\ No newline at end of file
Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImplementor.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImplementor.java (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImplementor.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -0,0 +1,14 @@
+package org.hibernate.validation.engine;
+
+import javax.validation.ValidatorFactory;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface ValidatorFactoryImplementor extends ValidatorFactory {
+ /**
+ * Gives access to the required parsed meta data.
+ * This never returns an null object
+ */
+ <T> MetaDataProviderImpl<T> getMetadataProvider(Class<T> clazz);
+}
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -27,23 +27,19 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
+import javax.validation.BeanDescriptor;
import javax.validation.ConstraintDescriptor;
-import javax.validation.ConstraintFactory;
import javax.validation.ConstraintViolation;
import javax.validation.MessageResolver;
-import javax.validation.Validator;
import javax.validation.PropertyDescriptor;
-import javax.validation.BeanDescriptor;
+import javax.validation.Validator;
-import org.hibernate.validation.Version;
import org.hibernate.validation.ValidatorConstants;
+import org.hibernate.validation.Version;
import org.hibernate.validation.impl.ConstraintDescriptorImpl;
-import org.hibernate.validation.impl.ConstraintFactoryImpl;
import org.hibernate.validation.impl.ConstraintViolationImpl;
-import org.hibernate.validation.impl.ResourceBundleMessageResolver;
-import org.hibernate.validation.util.ReflectionHelper;
import org.hibernate.validation.util.PropertyIterator;
+import org.hibernate.validation.util.ReflectionHelper;
/**
* The main Bean Validation class.
@@ -52,9 +48,9 @@
* @author Hardy Ferentschik
* @todo Make all properties transient for serializability.
*/
-public class ValidatorImpl<T> implements Validator<T> {
+public class ValidatorImpl implements Validator {
- private static final Set<Class> INDEXABLE_CLASS = new HashSet<Class>();
+ private static final Set<Class<?>> INDEXABLE_CLASS = new HashSet<Class<?>>();
static {
INDEXABLE_CLASS.add( Integer.class );
@@ -62,46 +58,36 @@
INDEXABLE_CLASS.add( String.class );
}
+
+ //TODO move to Factory at least
static {
Version.touch();
}
- @SuppressWarnings("unchecked")
- private final List<ConstraintViolationImpl<T>> EMPTY_CONSTRAINTS_LIST = Collections.EMPTY_LIST;
-
+ //TODO remove
/**
* A map for caching validators for cascaded entities.
*/
- private final Map<Class<?>, ValidatorImpl> subValidators = new ConcurrentHashMap<Class<?>, ValidatorImpl>();
+ //private final Map<Class<?>, ValidatorImpl> subValidators = new ConcurrentHashMap<Class<?>, ValidatorImpl>();
+
/**
- * Gives access to the required parsed meta data.
- */
- private final MetaDataProvider<T> metaDataProvider;
-
- /**
* Message resolver used for interpolating error messages.
*/
private final MessageResolver messageResolver;
+
+ private final ValidatorFactoryImplementor factory;
- public ValidatorImpl(Class<T> beanClass, ConstraintFactory constraintFactory, MessageResolver messageResolver) {
- if ( beanClass == null ) {
- throw new IllegalArgumentException( "Bean class paramter cannot be null" );
- }
-
- metaDataProvider = new MetaDataProviderImpl<T>( beanClass, constraintFactory );
+ public ValidatorImpl(ValidatorFactoryImplementor factory, MessageResolver messageResolver) {
+ this.factory = factory;
this.messageResolver = messageResolver;
}
- public ValidatorImpl(Class<T> beanClass) {
- this( beanClass, new ConstraintFactoryImpl(), new ResourceBundleMessageResolver() );
- }
-
/**
* {@inheritDoc}
*/
- public Set<ConstraintViolation<T>> validate(T object, String... groups) {
+ public <T> Set<ConstraintViolation<T>> validate(T object, String... groups) {
if ( object == null ) {
throw new IllegalArgumentException( "Validation of a null object" );
}
@@ -123,9 +109,9 @@
* @todo Currently we iterate the cascaded fields multiple times. Maybe we should change to an approach where we iterate the object graph only once.
* @todo Context root bean can be a different object than the current Validator<T> hence two different generics variables
*/
- private List<ConstraintViolationImpl<T>> validate(ValidationContext<T> context, List<String> groups) {
+ private <T> List<ConstraintViolationImpl<T>> validate(ValidationContext<T> context, List<String> groups) {
if ( context.peekValidatedObject() == null ) {
- return EMPTY_CONSTRAINTS_LIST;
+ return Collections.emptyList();
}
// if no group is specified use the default
@@ -137,7 +123,7 @@
boolean isGroupSequence;
for ( String group : groups ) {
expandedGroups = new ArrayList<String>();
- isGroupSequence = expandGroupName( group, expandedGroups );
+ isGroupSequence = expandGroupName( context.peekValidatedObjectType(), group, expandedGroups );
for ( String expandedGroupName : expandedGroups ) {
context.setCurrentGroup( expandedGroupName );
@@ -158,7 +144,11 @@
*
* @param context The current validation context.
*/
- private void validateConstraints(ValidationContext<T> context) {
+ private <T> void validateConstraints(ValidationContext<T> context) {
+ //casting rely on the fact that root object is at the top of the stack
+ @SuppressWarnings( "unchecked" )
+ MetaDataProviderImpl<T> metaDataProvider =
+ ( MetaDataProviderImpl<T> ) factory.getMetadataProvider( context.peekValidatedObjectType() );
for ( MetaConstraint metaConstraint : metaDataProvider.getConstraintMetaDataList() ) {
ConstraintDescriptorImpl constraintDescriptor = metaConstraint.getDescriptor();
context.pushProperty( metaConstraint.getPropertyName() );
@@ -197,7 +187,7 @@
context.markProcessedForCurrentGroup();
}
- private void validateCascadedConstraint(ValidationContext<T> context, Type type, Object value) {
+ private <T> void validateCascadedConstraint(ValidationContext<T> context, Type type, Object value) {
if ( value == null ) {
return;
}
@@ -226,8 +216,8 @@
validateCascadedConstraint( context, iter );
}
- private void validateCascadedConstraints(ValidationContext<T> context) {
- List<Member> cascadedMembers = getMetaDataProvider().getCascadedMembers();
+ private <T> void validateCascadedConstraints(ValidationContext<T> context) {
+ List<Member> cascadedMembers = factory.getMetadataProvider( context.peekValidatedObjectType() ).getCascadedMembers();
for ( Member member : cascadedMembers ) {
Type type = ReflectionHelper.typeOf( member );
context.pushProperty( ReflectionHelper.getPropertyName( member ) );
@@ -238,7 +228,7 @@
}
}
- private void validateCascadedConstraint(ValidationContext<T> context, Iterator<?> iter) {
+ private <T> void validateCascadedConstraint(ValidationContext<T> context, Iterator<?> iter) {
Object actualValue;
String propertyIndex;
int i = 0;
@@ -260,10 +250,8 @@
context.replacePropertyIndex( propertyIndex );
- Class cascadedClass = actualValue.getClass();
- ValidatorImpl validatorImpl = getValidatorForClass( cascadedClass );
context.pushValidatedObject( actualValue );
- validatorImpl.validate( context, Arrays.asList( context.getCurrentGroup() ) );
+ validate( context, Arrays.asList( context.getCurrentGroup() ) );
context.popValidatedObject();
i++;
}
@@ -273,16 +261,19 @@
/**
* {@inheritDoc}
*/
- public Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, String... groups) {
+ public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, String... groups) {
List<ConstraintViolationImpl<T>> failingConstraintViolations = new ArrayList<ConstraintViolationImpl<T>>();
validateProperty( object, new PropertyIterator( propertyName ), failingConstraintViolations, groups );
return new HashSet<ConstraintViolation<T>>( failingConstraintViolations );
}
- private void validateProperty(T object, PropertyIterator propertyIter, List<ConstraintViolationImpl<T>> failingConstraintViolations, String... groups) {
- DesrciptorValueWrapper wrapper = getConstraintDescriptorAndValueForPath( this, propertyIter, object );
+ private <T> void validateProperty(T object, PropertyIterator propertyIter, List<ConstraintViolationImpl<T>> failingConstraintViolations, String... groups) {
+ @SuppressWarnings( "unchecked" )
+ final Class<T> beanType = (Class<T>) object.getClass();
+ DesrciptorValueWrapper wrapper = getConstraintDescriptorAndValueForPath( beanType, propertyIter, object );
+
if ( wrapper == null ) {
return;
}
@@ -296,7 +287,7 @@
boolean isGroupSequence;
for ( String group : groups ) {
expandedGroups = new ArrayList<String>();
- isGroupSequence = expandGroupName( group, expandedGroups );
+ isGroupSequence = expandGroupName( beanType, group, expandedGroups );
for ( String expandedGroupName : expandedGroups ) {
@@ -316,7 +307,7 @@
ConstraintViolationImpl<T> failingConstraintViolation = new ConstraintViolationImpl<T>(
message,
object,
- ( Class<T> ) object.getClass(),
+ beanType,
object,
wrapper.value,
propertyIter.getOriginalProperty(), //FIXME use error.getProperty()
@@ -337,15 +328,15 @@
/**
* {@inheritDoc}
*/
- public Set<ConstraintViolation<T>> validateValue(String propertyName, Object value, String... groups) {
+ public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, String... groups) {
List<ConstraintViolationImpl<T>> failingConstraintViolations = new ArrayList<ConstraintViolationImpl<T>>();
- validateValue( value, new PropertyIterator( propertyName ), failingConstraintViolations, groups );
+ validateValue( beanType, value, new PropertyIterator( propertyName ), failingConstraintViolations, groups );
return new HashSet<ConstraintViolation<T>>( failingConstraintViolations );
}
- private void validateValue(Object object, PropertyIterator propertyIter, List<ConstraintViolationImpl<T>> failingConstraintViolations, String... groups) {
- ConstraintDescriptorImpl constraintDescriptor = getConstraintDescriptorForPath( this, propertyIter );
+ private <T> void validateValue(Class<T> beanType, Object object, PropertyIterator propertyIter, List<ConstraintViolationImpl<T>> failingConstraintViolations, String... groups) {
+ ConstraintDescriptorImpl constraintDescriptor = getConstraintDescriptorForPath( beanType, propertyIter );
if ( constraintDescriptor == null ) {
return;
@@ -360,7 +351,7 @@
boolean isGroupSequence;
for ( String group : groups ) {
expandedGroups = new ArrayList<String>();
- isGroupSequence = expandGroupName( group, expandedGroups );
+ isGroupSequence = expandGroupName( beanType, group, expandedGroups );
for ( String expandedGroupName : expandedGroups ) {
@@ -407,18 +398,18 @@
* a constraint descriptor will be returned.
* </p>
*
- * @param validator the validator to check for constraints.
+ * @param clazz the class type to check for constraints.
* @param propertyIter an instance of <code>PropertyIterator</code>
*
* @return The constraint descriptor matching the given path.
*/
- private ConstraintDescriptorImpl getConstraintDescriptorForPath(ValidatorImpl<?> validator, PropertyIterator propertyIter) {
+ private ConstraintDescriptorImpl getConstraintDescriptorForPath(Class<?> clazz, PropertyIterator propertyIter) {
ConstraintDescriptorImpl matchingConstraintDescriptor = null;
propertyIter.split();
if ( !propertyIter.hasNext() ) {
- List<MetaConstraint> metaConstraintList = validator.getMetaDataProvider().getConstraintMetaDataList();
+ List<MetaConstraint> metaConstraintList = factory.getMetadataProvider(clazz).getConstraintMetaDataList();
for ( MetaConstraint metaConstraint : metaConstraintList ) {
ConstraintDescriptor constraintDescriptor = metaConstraint.getDescriptor();
if ( metaConstraint.getPropertyName().equals( propertyIter.getHead() ) ) {
@@ -427,7 +418,7 @@
}
}
else {
- List<Member> cascadedMembers = validator.getMetaDataProvider().getCascadedMembers();
+ List<Member> cascadedMembers = factory.getMetadataProvider(clazz).getCascadedMembers();
for ( Member m : cascadedMembers ) {
if ( ReflectionHelper.getPropertyName( m ).equals( propertyIter.getHead() ) ) {
Type type = ReflectionHelper.typeOf( m );
@@ -439,8 +430,7 @@
}
}
- ValidatorImpl v = getValidatorForClass( ( Class ) type );
- matchingConstraintDescriptor = v.getConstraintDescriptorForPath( v, propertyIter );
+ matchingConstraintDescriptor = getConstraintDescriptorForPath( (Class<?>) type, propertyIter );
}
}
}
@@ -449,7 +439,7 @@
}
- private DesrciptorValueWrapper getConstraintDescriptorAndValueForPath(ValidatorImpl<?> validator, PropertyIterator propertyIter, Object value) {
+ private DesrciptorValueWrapper getConstraintDescriptorAndValueForPath(Class<?> clazz, PropertyIterator propertyIter, Object value) {
DesrciptorValueWrapper wrapper = null;
propertyIter.split();
@@ -457,7 +447,7 @@
// bottom out - there is only one token left
if ( !propertyIter.hasNext() ) {
- List<MetaConstraint> metaConstraintList = validator.getMetaDataProvider().getConstraintMetaDataList();
+ List<MetaConstraint> metaConstraintList = factory.getMetadataProvider(clazz).getConstraintMetaDataList();
for ( MetaConstraint metaConstraint : metaConstraintList ) {
ConstraintDescriptor constraintDescriptor = metaConstraint.getDescriptor();
if ( metaConstraint.getPropertyName().equals( propertyIter.getHead() ) ) {
@@ -468,20 +458,19 @@
}
}
else {
- List<Member> cascadedMembers = validator.getMetaDataProvider().getCascadedMembers();
+ List<Member> cascadedMembers = factory.getMetadataProvider(clazz).getCascadedMembers();
for ( Member m : cascadedMembers ) {
if ( ReflectionHelper.getPropertyName( m ).equals( propertyIter.getHead() ) ) {
ReflectionHelper.setAccessibility( m );
- Object newValue = null;
+ Object newValue;
if ( propertyIter.isIndexed() ) {
newValue = ReflectionHelper.getValue( m, value );
}
else {
newValue = ReflectionHelper.getIndexedValue( value, propertyIter.getIndex() );
}
- ValidatorImpl cascadedValidator = getValidatorForClass( newValue.getClass() );
- wrapper = cascadedValidator.getConstraintDescriptorAndValueForPath(
- cascadedValidator, propertyIter, newValue
+ wrapper = getConstraintDescriptorAndValueForPath(
+ newValue.getClass(), propertyIter, newValue
);
}
}
@@ -491,7 +480,7 @@
}
- private void addFailingConstraint(List<ConstraintViolationImpl<T>> failingConstraintViolations, ConstraintViolationImpl<T> failingConstraintViolation) {
+ private <T> void addFailingConstraint(List<ConstraintViolationImpl<T>> failingConstraintViolations, ConstraintViolationImpl<T> failingConstraintViolation) {
int i = failingConstraintViolations.indexOf( failingConstraintViolation );
if ( i == -1 ) {
failingConstraintViolations.add( failingConstraintViolation );
@@ -505,26 +494,23 @@
/**
* @todo add child validation
*/
- public boolean hasConstraints() {
- return metaDataProvider.getConstraintMetaDataList().size() > 0;
+ public boolean hasConstraints(Class<?> clazz) {
+ return factory.getMetadataProvider( clazz ).getConstraintMetaDataList().size() > 0;
}
- public BeanDescriptor getConstraintsForClass() {
- return metaDataProvider.getBeanDescriptor();
+ public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
+ return factory.getMetadataProvider( clazz ).getBeanDescriptor();
}
- public PropertyDescriptor getConstraintsForProperty(String propertyName) {
- return metaDataProvider.getPropertyDescriptors().get( propertyName );
+ public PropertyDescriptor getConstraintsForProperty(Class<?> clazz, String propertyName) {
+ return factory.getMetadataProvider( clazz ).getPropertyDescriptors().get( propertyName );
}
- public Set<String> getPropertiesWithConstraints() {
- return Collections.unmodifiableSet( metaDataProvider.getPropertyDescriptors()
- .keySet() );
+ public Set<String> getPropertiesWithConstraints(Class<?> clazz) {
+ final MetaDataProviderImpl<?> dataProvider = factory.getMetadataProvider( clazz );
+ return Collections.unmodifiableSet( dataProvider.getPropertyDescriptors().keySet() );
}
- public MetaDataProvider<T> getMetaDataProvider() {
- return metaDataProvider;
- }
/**
* Checks whether the provided group name is a group sequence and if so expands the group name and add the expanded
@@ -536,13 +522,13 @@
*
* @return <code>true</code> if an expansion took place, <code>false</code> otherwise.
*/
- private boolean expandGroupName(String groupName, List<String> expandedGroupNames) {
+ private <T> boolean expandGroupName(Class<T> beanType, String groupName, List<String> expandedGroupNames) {
if ( expandedGroupNames == null ) {
throw new IllegalArgumentException( "List cannot be empty" );
}
boolean isGroupSequence;
-
+ MetaDataProviderImpl<T> metaDataProvider = factory.getMetadataProvider( beanType );
if ( metaDataProvider.getGroupSequences().containsKey( groupName ) ) {
expandedGroupNames.addAll( metaDataProvider.getGroupSequences().get( groupName ) );
isGroupSequence = true;
@@ -554,16 +540,6 @@
return isGroupSequence;
}
- @SuppressWarnings("unchecked")
- private <V> ValidatorImpl<V> getValidatorForClass(Class<V> cascadedClass) {
- ValidatorImpl<V> validatorImpl = subValidators.get( cascadedClass );
- if ( validatorImpl == null ) {
- validatorImpl = new ValidatorImpl<V>( cascadedClass );
- subValidators.put( cascadedClass, validatorImpl );
- }
- return validatorImpl;
- }
-
private class DesrciptorValueWrapper {
final ConstraintDescriptorImpl descriptor;
final Object value;
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -17,6 +17,8 @@
*/
package org.hibernate.validation.impl;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import javax.validation.ConstraintFactory;
import javax.validation.MessageResolver;
import javax.validation.Validator;
@@ -24,17 +26,23 @@
import javax.validation.spi.ValidatorFactoryConfiguration;
import org.hibernate.validation.engine.ValidatorImpl;
+import org.hibernate.validation.engine.ValidatorFactoryImplementor;
+import org.hibernate.validation.engine.MetaDataProviderImpl;
/**
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
-public class ValidatorFactoryImpl implements ValidatorFactory {
+public class ValidatorFactoryImpl implements ValidatorFactoryImplementor {
private final MessageResolver messageResolver;
private final ConstraintFactory constraintFactory;
+ //TODO is there a way to replace ? by so kind of <T> to express the correlation?
+ private Map<Class<?>, MetaDataProviderImpl<?>> metadataProviders
+ = new ConcurrentHashMap<Class<?>, MetaDataProviderImpl<?>>(10);
+
public ValidatorFactoryImpl(ValidatorFactoryConfiguration configuration) {
this.messageResolver = configuration.getMessageResolver();
this.constraintFactory = configuration.getConstraintFactory();
@@ -44,15 +52,27 @@
/**
* {@inheritDoc}
*/
- public <T> Validator<T> getValidator(Class<T> clazz) {
- return new ValidatorImpl<T>( clazz, constraintFactory, messageResolver );
+ public Validator getValidator() {
+ return new ValidatorImpl( this, messageResolver );
}
- public <T> Validator<T> getValidator(Class<T> clazz, MessageResolver messageResolver) {
- return new ValidatorImpl<T>( clazz, constraintFactory, messageResolver );
+ public Validator getValidator(MessageResolver messageResolver) {
+ return new ValidatorImpl( this, messageResolver );
}
public MessageResolver getMessageResolver() {
return messageResolver;
}
+
+ public <T> MetaDataProviderImpl<T> getMetadataProvider(Class<T> beanClass) {
+ //FIXME make sure a optimized mock is provided when no constraints are present.
+ if (beanClass == null) throw new IllegalArgumentException( "Class cannot be null" );
+ @SuppressWarnings( "unchecked")
+ MetaDataProviderImpl<T> metadata = ( MetaDataProviderImpl<T> ) metadataProviders.get(beanClass);
+ if (metadata == null) {
+ metadata = new MetaDataProviderImpl<T>(beanClass, constraintFactory);
+ metadataProviders.put( beanClass, metadata );
+ }
+ return metadata;
+ }
}
Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -77,7 +77,7 @@
assertDefaultBuilderAndFactory( builder );
ValidatorFactory factory = builder.build();
- Validator<Customer> validator = factory.getValidator( Customer.class );
+ Validator validator = factory.getValidator();
Customer customer = new Customer();
customer.setFirstName( "John" );
@@ -100,7 +100,7 @@
assertDefaultBuilderAndFactory( builder );
ValidatorFactory factory = builder.build();
- Validator<Customer> validator = factory.getValidator( Customer.class );
+ Validator validator = factory.getValidator( );
Customer customer = new Customer();
customer.setFirstName( "John" );
@@ -124,7 +124,7 @@
}
);
factory = builder.build();
- validator = factory.getValidator( Customer.class );
+ validator = factory.getValidator( );
constraintViolations = validator.validate( customer );
assertEquals( "Wrong number of constraints", 1, constraintViolations.size() );
constraintViolation = constraintViolations.iterator().next();
@@ -138,7 +138,7 @@
assertDefaultBuilderAndFactory( builder );
ValidatorFactory factory = builder.build();
- Validator<Customer> validator = factory.getValidator( Customer.class );
+ Validator validator = factory.getValidator( );
Customer customer = new Customer();
customer.setFirstName( "John" );
@@ -161,7 +161,7 @@
}
);
factory = builder.build();
- validator = factory.getValidator( Customer.class );
+ validator = factory.getValidator( );
constraintViolations = validator.validate( customer );
assertEquals( "Wrong number of constraints", 0, constraintViolations.size() );
}
Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorImplTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorImplTest.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorImplTest.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -22,6 +22,7 @@
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
import javax.validation.Validator;
+import javax.validation.Validation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -40,6 +41,7 @@
import org.hibernate.validation.eg.EnglishDictonary;
import org.hibernate.validation.eg.Order;
import org.hibernate.validation.eg.Unconstraint;
+import org.hibernate.validation.HibernateValidatorFactoryBuilder;
/**
* Tests for the implementation of <code>Validator</code>.
@@ -48,14 +50,27 @@
*/
public class ValidatorImplTest {
+ private Validator hibernateValidator;
+ //TODO should not be Hibernate specific in most case
+ private Validator getHibernateValidator() {
+ if (hibernateValidator == null) {
+ HibernateValidatorFactoryBuilder builder = Validation
+ .builderType( HibernateValidatorFactoryBuilder.class )
+ .getBuilder();
+ hibernateValidator = builder.build().getValidator();
+ }
+ return hibernateValidator;
+ }
+
+
/**
* JSR 303: Requirements on classes to be validates (3.1)
*/
@Test
public void testWrongMethodName() {
try {
- new ValidatorImpl<Boy>( Boy.class );
+ getHibernateValidator().hasConstraints( Boy.class );
fail();
}
catch ( ValidationException e ) {
@@ -70,36 +85,36 @@
@Test(expected = IllegalArgumentException.class)
public void testNullParamterToValidatorImplConstructor() {
- new ValidatorImpl<Unconstraint>( null );
+ getHibernateValidator().hasConstraints( null );
}
@Test
public void testUnconstraintClass() {
- Validator<Unconstraint> validator = new ValidatorImpl<Unconstraint>( Unconstraint.class );
- assertTrue( "There should be no constraints", !validator.hasConstraints() );
+ Validator validator = getHibernateValidator();
+ assertTrue( "There should be no constraints", !validator.hasConstraints( Unconstraint.class ) );
}
@Test
public void testHasConstraints() {
- Validator<Customer> validatorCustomer = new ValidatorImpl<Customer>( Customer.class );
- assertTrue( "There should be constraints", validatorCustomer.hasConstraints() );
+ Validator validator = getHibernateValidator();
+ assertTrue( "There should be constraints", validator.hasConstraints( Customer.class ) );
}
@Test(expected = IllegalArgumentException.class)
public void testValidateWithNull() {
- Validator<Customer> validatorCustomer = new ValidatorImpl<Customer>( Customer.class );
- validatorCustomer.validate( null );
+ Validator validator = getHibernateValidator();
+ validator.validate( null );
}
@Test(expected = IllegalArgumentException.class)
public void testValidateWithNullProperty() {
- Validator<Customer> validatorCustomer = new ValidatorImpl<Customer>( Customer.class );
- validatorCustomer.validate( null, "firstName" );
+ Validator validator = getHibernateValidator();
+ validator.validate( null, "firstName" );
}
@Test
public void testGroups() {
- Validator<Book> validator = new ValidatorImpl<Book>( Book.class );
+ Validator validator = getHibernateValidator();
Author author = new Author();
author.setLastName( "" );
@@ -155,7 +170,7 @@
@Test
public void testDefaultGroupSequence() {
- Validator<Book> validator = new ValidatorImpl<Book>( Book.class );
+ Validator validator = getHibernateValidator();
Author author = new Author();
author.setLastName( "" );
@@ -198,7 +213,7 @@
@Test
public void testBasicValidation() {
- Validator<Customer> validator = new ValidatorImpl<Customer>( Customer.class );
+ Validator validator = getHibernateValidator();
Customer customer = new Customer();
customer.setFirstName( "John" );
@@ -214,7 +229,7 @@
@Test
public void testGroupSequences() {
- Validator<Dictonary> validator = new ValidatorImpl<Dictonary>( Dictonary.class );
+ Validator validator = getHibernateValidator();
Dictonary dictonary = new Dictonary();
dictonary.setTitle( "English - German" );
@@ -230,7 +245,7 @@
@Test
public void testValidationFailureInMultipleGroups() {
- Validator<Animal> validator = new ValidatorImpl<Animal>( Animal.class );
+ Validator validator = getHibernateValidator();
Animal elepfant = new Animal();
elepfant.setName( "" );
elepfant.setDomain( Animal.Domain.EUKARYOTA );
@@ -255,12 +270,13 @@
@Test(expected = ValidationException.class)
public void testInvalidSequenceName() {
- new ValidatorImpl<EnglishDictonary>( EnglishDictonary.class );
+ Validator validator = getHibernateValidator();
+ validator.hasConstraints( EnglishDictonary.class );
}
@Test
public void testValidationMethod() {
- Validator<Address> validator = new ValidatorImpl<Address>( Address.class );
+ Validator validator = getHibernateValidator();
Address address = new Address();
address.setAddressline1( null );
@@ -288,7 +304,7 @@
constraintViolations.size()
);
- constraintViolations = validator.validateValue( "city", "Paris" );
+ constraintViolations = validator.validateValue( Address.class, "city", "Paris" );
assertEquals(
"Paris should be a valid city name.",
0,
@@ -298,7 +314,7 @@
@Test
public void testValidateList() {
- Validator<Customer> validator = new ValidatorImpl<Customer>( Customer.class );
+ Validator validator = getHibernateValidator();
Customer customer = new Customer();
customer.setFirstName( "John" );
@@ -326,7 +342,7 @@
*/
@Test
public void testMultiValueConstraint() {
- Validator<Engine> validator = new ValidatorImpl<Engine>( Engine.class );
+ Validator validator = getHibernateValidator();
Engine engine = new Engine();
engine.setSerialNumber( "mail(a)foobar.com" );
@@ -348,6 +364,8 @@
*/
@Test
public void testGraphValidation() {
+ Validator validator = getHibernateValidator();
+
Actor clint = new Actor( "Clint", "Eastwood" );
Actor morgan = new Actor( "Morgan", "" );
Actor charlie = new Actor( "Charlie", "Sheen" );
@@ -359,7 +377,7 @@
morgan.addPlayedWith( clint );
clint.addPlayedWith( morgan );
- Validator<Actor> validator = new ValidatorImpl<Actor>( Actor.class );
+
Set<ConstraintViolation<Actor>> constraintViolations = validator.validate( clint );
ConstraintViolation constraintViolation = constraintViolations.iterator().next();
assertEquals( "Wrong number of constraints", 1, constraintViolations.size() );
@@ -372,12 +390,12 @@
@Test
public void testValidateValue() {
- Validator<Customer> validator = new ValidatorImpl<Customer>( Customer.class );
+ Validator validator = getHibernateValidator();
Order order = new Order();
Set<ConstraintViolation<Customer>> constraintViolations = validator.validateValue(
- "orderList[0].orderNumber", null
+ Customer.class, "orderList[0].orderNumber", null
);
assertEquals( "Wrong number of constraints", 1, constraintViolations.size() );
@@ -389,7 +407,7 @@
assertEquals( "Wrong value", order.getOrderNumber(), constraintViolation.getInvalidValue() );
assertEquals( "Wrong propertyName", "orderList[0].orderNumber", constraintViolation.getPropertyPath() );
- constraintViolations = validator.validateValue( "orderList[0].orderNumber", "1234" );
+ constraintViolations = validator.validateValue( Customer.class, "orderList[0].orderNumber", "1234" );
assertEquals( "Wrong number of constraints", 0, constraintViolations.size() );
}
}
Modified: validator/trunk/validation-api/src/main/java/javax/validation/Validator.java
===================================================================
--- validator/trunk/validation-api/src/main/java/javax/validation/Validator.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/validation-api/src/main/java/javax/validation/Validator.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -21,14 +21,14 @@
import java.util.Set;
/**
- * Validate a given object type.
+ * Validate objects
* Implementations of this interface must be thread-safe
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
* @todo Should Serializable be part of the definition?
*/
-public interface Validator<T> extends Serializable {
+public interface Validator extends Serializable {
/**
* validate all constraints on object
*
@@ -40,7 +40,7 @@
*
* @throws IllegalArgumentException e if object is null
*/
- Set<ConstraintViolation<T>> validate(T object, String... groups);
+ <T> Set<ConstraintViolation<T>> validate(T object, String... groups);
/**
* validate all constraints on <code>propertyName</code> property of object
@@ -54,7 +54,7 @@
*
* @throws IllegalArgumentException e if object is null
*/
- Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, String... groups);
+ <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, String... groups);
/**
* validate all constraints on <code>propertyName</code> property
@@ -69,32 +69,41 @@
*
* @return constraint violations or an empty Set if none
*/
- Set<ConstraintViolation<T>> validateValue(String propertyName, Object value, String... groups);
+ <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, String... groups);
/**
* return true if at least one constraint declaration is present for the given bean
* or if one property is marked for validation cascade
+ *
+ * @param clazz class type evaluated
*/
- boolean hasConstraints();
+ boolean hasConstraints(Class<?> clazz);
/**
* Return the class level constraints
* The returned object (and associated objects including ConstraintDescriptors)
* are immutable.
+ *
+ * @param clazz class type evaluated
*/
- BeanDescriptor getConstraintsForClass();
+ BeanDescriptor getConstraintsForClass(Class<?> clazz);
/**
* Return the property level constraints for a given propertyName
* or null if either the property does not exist or has no constraint
* The returned object (and associated objects including ConstraintDescriptors)
* are immutable.
+ *
+ * @param clazz class type evaluated
+ * @param propertyName property evaludated
*/
- PropertyDescriptor getConstraintsForProperty(String propertyName);
+ PropertyDescriptor getConstraintsForProperty(Class<?> clazz, String propertyName);
/**
* return the property names having at least a constraint defined
+ *
+ * @param clazz class type evaluated
*/
- Set<String> getPropertiesWithConstraints();
+ Set<String> getPropertiesWithConstraints(Class<?> clazz);
}
Modified: validator/trunk/validation-api/src/main/java/javax/validation/ValidatorFactory.java
===================================================================
--- validator/trunk/validation-api/src/main/java/javax/validation/ValidatorFactory.java 2008-11-19 17:34:11 UTC (rev 15599)
+++ validator/trunk/validation-api/src/main/java/javax/validation/ValidatorFactory.java 2008-11-19 17:38:55 UTC (rev 15600)
@@ -26,23 +26,21 @@
*/
public interface ValidatorFactory {
/**
- * return an initialized Validator instance for the specific class.
+ * return an initialized Validator instance.
* Validator instances can be pooled and shared by the implementation
*
- * @param clazz the object class the validator validates
*/
- <T> Validator<T> getValidator(Class<T> clazz);
+ Validator getValidator();
/**
- * return an initialized Validator instance for the specific class.
+ * return an initialized Validator instance.
* Validator instances can be pooled and shared by the implementation
*
* The returned Validator instance must use the MessageResolver instance
* passed as a parameter to resolve error messages.
*
- * @param clazz the object class the validator validates
*/
- <T> Validator<T> getValidator(Class<T> clazz, MessageResolver messageResolver);
+ Validator getValidator(MessageResolver messageResolver);
/**
* Returns the MessageResolver instance configured at initialization time
16 years
Hibernate SVN: r15599 - validator/tags.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2008-11-19 12:34:11 -0500 (Wed, 19 Nov 2008)
New Revision: 15599
Removed:
validator/tags/BeanValidatonBeforeUngenerifiedValidator/
Log:
type
16 years