Hibernate SVN: r10620 - trunk/HibernateExt/tools/doc/reference/en/modules
by hibernate-commits@lists.jboss.org
Author: max.andersen(a)jboss.com
Date: 2006-10-19 11:50:37 -0400 (Thu, 19 Oct 2006)
New Revision: 10620
Modified:
trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml
trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml
Log:
HBX-769 Improved Reference Manual Documentation (meta attribute advanced examples)
+ various fixes to doc
Modified: trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml
===================================================================
--- trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml 2006-10-19 14:15:34 UTC (rev 10619)
+++ trunk/HibernateExt/tools/doc/reference/en/modules/ant.xml 2006-10-19 15:50:37 UTC (rev 10620)
@@ -22,8 +22,9 @@
e.g. Hibernate 3.2 jar's with e.g. an Hibernate 3.1 project since
the output generated will work with previous Hibernate 3 versions.
</para>
-
- <section>
+ </section>
+
+ <section>
<title>The <literal><hibernatetool></literal> ant Task</title>
<para>To use the ant tasks you need to have the hibernatetool task
@@ -825,5 +826,4 @@
</section>
</section>
</section>
- </section>
</chapter>
Modified: trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml
===================================================================
--- trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml 2006-10-19 14:15:34 UTC (rev 10619)
+++ trunk/HibernateExt/tools/doc/reference/en/modules/codegen.xml 2006-10-19 15:50:37 UTC (rev 10620)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml version="1.0" encoding="UTF-8"?>
<chapter id="codegen">
<title>Controlling POJO code generation</title>
@@ -24,26 +24,26 @@
<para>The following example shows how to use various <meta>
attributes and the resulting java code.</para>
- <programlisting><![CDATA[<class name="Person">
- <meta attribute="class-description">
+ <programlisting><class name="Person">
+ <meta attribute="class-description">
Javadoc for the Person class
@author Frodo
- </meta>
- <meta attribute="implements">IAuditable</meta>
- <id name="id" type="long">
- <meta attribute="scope-set">protected</meta>
- <generator class="increment"/>
- </id>
- <property name="name" type="string">
- <meta attribute="field-description">The name of the person</meta>
- </property>
-</class>]]></programlisting>
+ </meta>
+ <meta attribute="implements">IAuditable</meta>
+ <id name="id" type="long">
+ <meta attribute="scope-set">protected</meta>
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string">
+ <meta attribute="field-description">The name of the person</meta>
+ </property>
+</class></programlisting>
<para>The above hbm.xml will produce something like the following (code
shortened for better understanding). Notice the Javadoc comment and the
protected set methods:</para>
- <programlisting><![CDATA[// default package
+ <programlisting>// default package
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
@@ -86,7 +86,7 @@
this.name = name;
}
-}]]></programlisting>
+}</programlisting>
<table frame="topbot">
<title>Supported meta tags</title>
@@ -244,5 +244,294 @@
attribute, e.g. <literal><meta attribute="scope-class"
inherit="false">public abstract</meta></literal> will restrict
the "class-scope" to the current class, not the subclasses.</para>
+
+ <section>
+ <title>Recomendations</title>
+
+ <para>The following are some good practices when using
+ <literal><meta></literal> attributes.</para>
+
+ <section>
+ <title>Dangers of a class level <literal>use-in-string and
+ use-in-equals</literal> meta attributes when having bi-directional
+ associations</title>
+
+ <para>If we have two entities with a bi-directional association
+ between them and define at class scope level the meta attributes:
+ <literal>use-in-string, use-in-equals</literal>:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="Person">
+ <meta attribute="use-in-tostring">true</meta>
+ <meta attribute="use-in-equals">true</meta>
+ ...
+ </class>
+</hibernate-mapping></programlisting>
+
+ <para>and for <literal>Event.hbm</literal> file:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="events.Event" table="EVENTS">
+ <meta attribute="use-in-tostring">true</meta>
+ <meta attribute="use-in-equals">true</meta>
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ <set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+ </set>
+ </class>
+</hibernate-mapping></programlisting>
+
+ <para>Then <literal><hbm2java></literal> will assume you want to
+ include all properties and collections in the
+ <literal>toString()/equals()</literal> methods and this can result in
+ infinite recursive calls.</para>
+
+ <para>To remedy this you have to decide which side of the association
+ will include the other part (if at all) in the
+ <literal>toString()/equals()</literal> methods. Therefore it is not a
+ good practice to put at class scope such meta attributes, unless you
+ are defining a class without bi-directional associations</para>
+
+ <para>We recomend instead to add the <literal>meta</literal>
+ attributes at the property level:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <meta attribute="use-in-tostring">true</meta>
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title">
+ <meta attribute="use-in-tostring">true</meta>
+ <meta attribute="use-in-equals">true</meta>
+ </property>
+ <set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+ </set>
+ </class>
+</hibernate-mapping></programlisting>
+
+ <para>and now for <literal>Person</literal>:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="Person">
+ <meta attribute="class-description">
+ Javadoc for the Person class
+ @author Frodo
+ </meta>
+ <meta attribute="implements">IAuditable</meta>
+ <id name="id" type="long">
+ <meta attribute="scope-set">protected</meta>
+ <meta attribute="use-in-tostring">true</meta>
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string">
+ <meta attribute="field-description">The name of the person</meta>
+ <meta attribute="use-in-tostring">true</meta>
+ </property>
+ </class>
+</hibernate-mapping></programlisting>
+ </section>
+
+ <section>
+ <title>Be aware of putting at class scope level
+ <literal><meta></literal> attribute
+ <literal>use-in-equals</literal></title>
+
+ <para>For <literal>equal()/hashCode()</literal> method generation, you
+ have to take into account that the attributes that participate on such
+ method definition, should take into account only attributes with
+ business meaning (the name, social security number, etc, but no
+ generated id's, for example).</para>
+
+ <para>This is important because Java's hashbased collections, such as
+ java.util.Set relies on equals() and hashcode() to be correct and not
+ change for objects in the set; this can be a problem if the id gets
+ assigned for an object after you inserted it into a set.</para>
+
+ <para>Therefore automatically configuration the generation of
+ <literal>equals()/hashCode()</literal> methods specifying at class
+ scope level the <literal><meta></literal> attribute
+ <literal>use-in-equals</literal> could be a dangerous decision that
+ could produce non expected side-effect.</para>
+
+ <para>See http://www.hibernate.org/109.html for an more in-depth
+ explanation on the subject of equals() and hashcode().</para>
+ </section>
+ </section>
+
+ <section>
+ <title>Advanced <literal><meta></literal> attribute
+ examples</title>
+
+ <para>This section shows an example for using meta attributes (including
+ userspecific attributes) together with the code generation features in
+ Hibernate Tools.</para>
+
+ <para>The usecase being implemented is to automatically insert some pre-
+ and post-conditions into the getter and setters of the generated POJO.
+ </para>
+
+ <section>
+ <title>Generate pre/post-conditions for methods</title>
+
+ <para>With an <literal><meta attribute="class-code"></literal>,
+ you can add addional methods on a given class, nevertheless such
+ <literal><meta></literal> attribute can not be used at property
+ scope level and Hibernatetools does not provide such
+ <literal><meta></literal> attributes.</para>
+
+ <para>A possibly solution for this is to modify the freemarker
+ templates responsable for generating the POJO's. If you look inside
+ <literal>hibernate-tools.jar</literal>, you can find the template:
+ <literal>pojo/PojoPropertyAccessor.ftl</literal></para>
+
+ <para>This file is as the named indicates used to generate property
+ accessors for pojo's.</para>
+
+ <para>Extract the <literal>PojoPropertyAccessor.ftl</literal> into a
+ local folder i.e. <literal>${hbm.template.path}</literal>, respecting
+ the whole path, for example:
+ <literal>${hbm.template.path}/pojo/PojoPropertyAccessor.ftl</literal></para>
+
+ <para>The contents of the file is something like this:</para>
+
+ <programlisting><#foreach property in pojo.getAllPropertiesIterator()>
+ ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(property, jdk5)} ${pojo.getGetterSignature(property)}() {
+ return this.${property.name};
+ }
+
+ ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyName(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}) {
+ this.${property.name} = ${property.name};
+ }
+</#foreach></programlisting>
+
+ <para>We can add conditionally pre/post-conditions on our
+ <literal>set</literal> method generation just adding a little
+ Freemarker syntax to the above source code:</para>
+
+ <programlisting><#foreach property in pojo.getAllPropertiesIterator()>
+ ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(property, jdk5)} ${pojo.getGetterSignature(property)}() {
+ return this.${property.name};
+ }
+
+ ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyName(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}) {
+ <#if pojo.hasMetaAttribute(property, "pre-cond")>
+ ${c2j.getMetaAsString(property, "pre-cond","\n")}
+ </#if>
+ this.${property.name} = ${property.name};
+ <#if pojo.hasMetaAttribute(property, "post-cond")>
+ ${c2j.getMetaAsString(property, "post-cond","\n")}
+ </#if>
+}
+</#foreach>
+</programlisting>
+
+ <para>Now if in any <literal>*hbm.xml</literal> file we define the
+ <literal><meta></literal> attributes:
+ <literal>pre-cond</literal> or <literal>post-cond</literal>, their
+ contents will be generated into the body of the relevant
+ <literal>set</literal> method.</para>
+
+ <para>As an examlpe let us add a pre-condition for property
+ <literal>name</literal> preventing no <literal>Person</literal> can
+ have an empty name. So we have to modify the
+ <literal>Person.hbm.xml</literal> file like this:</para>
+
+ <programlisting><hibernate-mapping>
+ <class name="Person">
+ <id name="id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="firstName" type="string">
+ <meta attribute="pre-cond"><![CDATA[
+ if ((firstName != null) && (firstName.length() == 0) ) {
+ throw new IllegalArgumentException("firstName can not be an empty String");
+ }]]>
+ </meta>
+ </property>
+</class>
+</hibernate-mapping></programlisting>
+
+ <para>Notes: i) If you don't use <literal><[[CDATA[]]></literal>
+ you have to scape the & symbol, i.e.: &amp; ii). Note that we
+ are referring to "firstName" directly and this is the parameter name
+ not the actual field name. If you want to refer the field you have to
+ use "this.firstName" instead. </para>
+
+ <para>Finally we have to generate the <literal>Person.java</literal>
+ class, for this we can use both Eclipse and Ant as long as you
+ remember to set or fill in the templatepath setting. For Ant we
+ configure <literal><hibernatetool></literal> task via
+ <literal>the templatepath</literal> attribute as in:</para>
+
+ <programlisting>
+ <target name="hbm2java">
+ <taskdef name="hibernatetool"
+ classname="org.hibernate.tool.ant.HibernateToolTask"
+ classpathref="lib.classpath"/>
+ <hibernatetool destdir="${hbm2java.dest.dir}"
+ templatepath="${hbm.template.path}">
+ <classpath>
+ <path refid="pojo.classpath"/>
+ </classpath>
+ <configuration>
+ <fileset dir="${hbm2java.src.dir}">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </configuration>
+ <hbm2java/>
+ </hibernatetool>
+ </target></programlisting>
+
+ <para>Invoking the target <literal><hbm2java></literal> will
+ generate on the <literal>${hbm2java.dest.dir}</literal> the file:
+ <literal>Person.java</literal>:</para>
+
+ <programlisting>// default package
+import java.io.Serializable;
+public class Person implements Serializable {
+
+ public Long id;
+
+ public String name;
+
+ public Person(java.lang.String name) {
+ this.name = name;
+ }
+
+ public Person() {
+ }
+
+ public java.lang.Long getId() {
+ return this.id;
+ }
+
+ public void setId(java.lang.Long id) {
+ this.id = id;
+ }
+
+ public java.lang.String getName() {
+ return this.name;
+ }
+
+ public void setName(java.lang.String name) {
+ if ((name != null) && (name.length() == 0)) {
+ throw new IllegalArgumentException("name can not be an empty String");
+ }
+ this.name = name;
+ }
+}</programlisting>
+
+ <para></para>
+ </section>
+ </section>
</section>
</chapter>
\ No newline at end of file
17 years, 6 months
Hibernate SVN: r10619 - branches/Branch_3_2/Hibernate3
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-19 10:15:34 -0400 (Thu, 19 Oct 2006)
New Revision: 10619
Modified:
branches/Branch_3_2/Hibernate3/build.xml
Log:
correct version on 3.2 branch
Modified: branches/Branch_3_2/Hibernate3/build.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/build.xml 2006-10-19 14:15:11 UTC (rev 10618)
+++ branches/Branch_3_2/Hibernate3/build.xml 2006-10-19 14:15:34 UTC (rev 10619)
@@ -23,7 +23,7 @@
<property name="version.major" value="3"/>
<property name="version.minor" value="2"/>
<property name="version.micro" value="0"/>
- <property name="version.qualifier" value="cr5"/>
+ <property name="version.qualifier" value="ga"/>
<property name="version.full" value="${version.major}.${version.minor}.${version.micro}.${version.qualifier}"/>
<property name="version.major_minor" value="${version.major}.${version.minor}"/>
<property name="fullname" value="${name}-${version.full}"/>
17 years, 6 months
Hibernate SVN: r10618 - branches/Branch_3_2/Hibernate3/src/org/hibernate/cfg
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-19 10:15:11 -0400 (Thu, 19 Oct 2006)
New Revision: 10618
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/cfg/Environment.java
Log:
correct version on 3.2 branch
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/cfg/Environment.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/cfg/Environment.java 2006-10-19 13:43:30 UTC (rev 10617)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/cfg/Environment.java 2006-10-19 14:15:11 UTC (rev 10618)
@@ -153,7 +153,7 @@
*/
public final class Environment {
- public static final String VERSION = "3.2.0.cr5";
+ public static final String VERSION = "3.2.0";
/**
* <tt>ConnectionProvider</tt> implementor to use when obtaining connections
17 years, 6 months
Hibernate SVN: r10617 - branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-19 09:43:30 -0400 (Thu, 19 Oct 2006)
New Revision: 10617
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java
Log:
decrease log level
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java 2006-10-19 13:43:04 UTC (rev 10616)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java 2006-10-19 13:43:30 UTC (rev 10617)
@@ -346,7 +346,7 @@
}
else {
String[] columns = propertyMapping.toColumns( path );
- log.warn( "Using non-qualified column reference [" + path + " -> (" + ArrayHelper.toString( columns ) + ")]" );
+ log.trace( "Using non-qualified column reference [" + path + " -> (" + ArrayHelper.toString( columns ) + ")]" );
return columns;
}
}
17 years, 6 months
Hibernate SVN: r10616 - trunk/Hibernate3/src/org/hibernate/hql/ast/tree
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-19 09:43:04 -0400 (Thu, 19 Oct 2006)
New Revision: 10616
Modified:
trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java
Log:
decrease log level
Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java 2006-10-19 03:27:04 UTC (rev 10615)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java 2006-10-19 13:43:04 UTC (rev 10616)
@@ -346,7 +346,7 @@
}
else {
String[] columns = propertyMapping.toColumns( path );
- log.warn( "Using non-qualified column reference [" + path + " -> (" + ArrayHelper.toString( columns ) + ")]" );
+ log.trace( "Using non-qualified column reference [" + path + " -> (" + ArrayHelper.toString( columns ) + ")]" );
return columns;
}
}
17 years, 6 months
Hibernate SVN: r10615 - in trunk/Hibernate3: src/org/hibernate/action src/org/hibernate/hql/ast src/org/hibernate/loader src/org/hibernate/loader/hql src/org/hibernate/param test/org/hibernate/test/hql
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-18 23:27:04 -0400 (Wed, 18 Oct 2006)
New Revision: 10615
Added:
trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
Modified:
trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
trunk/Hibernate3/src/org/hibernate/loader/Loader.java
trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
Log:
HHH-1774 : proper binding of multi-column parameter types to queries
Modified: trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -81,7 +81,7 @@
persister.getRootEntityName(),
session.getEntityMode(),
session.getFactory()
- );
+ );
lock = persister.getCache().lock(ck, previousVersion);
}
else {
@@ -99,41 +99,35 @@
instance,
rowId,
session
- );
+ );
}
-
EntityEntry entry = getSession().getPersistenceContext().getEntry( instance );
if ( entry == null ) {
throw new AssertionFailure( "possible nonthreadsafe access to session" );
}
if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
-
- // get the updated snapshot by cloning current state
- // it is safe to copy in place, since by this time
- // no-one else has a reference to the array
- TypeFactory.deepCopy(
- state,
- persister.getPropertyTypes(),
- persister.getPropertyCheckability(),
- state,
- session
- );
-
if ( persister.hasUpdateGeneratedProperties() ) {
+ // get the updated snapshot by cloning current state;
+ // it is safe to copy in place, since by this time
+ // no-one else has a reference to the array
+ TypeFactory.deepCopy(
+ state,
+ persister.getPropertyTypes(),
+ persister.getPropertyCheckability(),
+ state,
+ session
+ );
persister.processUpdateGeneratedProperties( id, instance, state, session );
if ( persister.isVersionPropertyGenerated() ) {
nextVersion = Versioning.getVersion(state, persister);
}
}
-
entry.postUpdate( instance, state, nextVersion );
-
}
if ( persister.hasCache() ) {
-
if ( persister.isCacheInvalidationRequired() || entry.getStatus()!=Status.MANAGED ) {
persister.getCache().evict(ck);
}
@@ -146,17 +140,14 @@
nextVersion,
getSession(),
instance
- );
- cacheEntry = persister.getCacheEntryStructure().structure(ce);
-// boolean put = persister.getCache().update(ck, cacheEntry);
+ );
+ cacheEntry = persister.getCacheEntryStructure().structure( ce );
boolean put = persister.getCache().update( ck, cacheEntry, nextVersion, previousVersion );
-
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatisticsImplementor()
.secondLevelCachePut( getPersister().getCache().getRegionName() );
}
}
-
}
postUpdate();
Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -60,6 +60,7 @@
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.param.VersionTypeSeedParameterSpecification;
+import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
@@ -87,98 +88,50 @@
* @see SqlASTFactory
*/
public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
- private static Log log = LogFactory.getLog( HqlSqlWalker.class );
+ private static final Log log = LogFactory.getLog( HqlSqlWalker.class );
- /**
- * A delegate that handles the Hiberanate meta data model.
- */
- private SessionFactoryHelper sessionFactoryHelper;
+ private final QueryTranslatorImpl queryTranslatorImpl;
+ private final HqlParser hqlParser;
+ private final SessionFactoryHelper sessionFactoryHelper;
+ private final Map tokenReplacements;
+ private final AliasGenerator aliasGenerator = new AliasGenerator();
+ private final LiteralProcessor literalProcessor;
+ private final ParseErrorHandler parseErrorHandler;
+ private final ASTPrinter printer;
+ private final String collectionFilterRole;
- /**
- * A delegate that handles literal constants.
- */
- private LiteralProcessor literalProcessor;
-
- /**
- * Handles parser errors.
- */
- private ParseErrorHandler parseErrorHandler;
-
- /**
- * The current context.
- */
private FromClause currentFromClause = null;
-
- /**
- * The top-level SelectClause.
- */
private SelectClause selectClause;
- /**
- * Generates alias names for tables.
- */
- private AliasGenerator aliasGenerator = new AliasGenerator();
-
- /**
- * The set of unique query spaces (a.k.a. table names).
- */
private Set querySpaces = new HashSet();
- /**
- * A (string->string) map is used to substitute function names and literals.
- */
- private Map tokenReplacements;
-
- private QueryTranslatorImpl queryTranslatorImpl;
-
- /**
- * The number of parameters encountered so far.
- */
private int parameterCount;
-
- /**
- * A map of lists which associates named and numbered parameters to a list of occurrences in the query.
- */
private Map namedParameters = new HashMap();
-
- /**
- * The filter collection role, or null if this isn't a filter compilation.
- */
- private String filterCollectionRole;
-
- /**
- * The parser that performed phase 1 - parse the HQL into an HQL tree.
- */
- private HqlParser hqlParser;
-
- private ASTPrinter printer;
-
- /**
- * The join type for any implied joins.
- */
- private int impliedJoinType;
-
private ArrayList parameters = new ArrayList();
private int numberOfParametersInSetClause;
private int positionalParameterCount;
private ArrayList assignmentSpecifications = new ArrayList();
+ private int impliedJoinType;
+
/**
* Create a new tree transformer.
*
- * @param qti Back pointer to the query translator implementation that is using this tree transform.
- * @param sfi The session factory implementor where the Hibernate mappings can be found.
- * @param parser
+ * @param qti Back pointer to the query translator implementation that is using this tree transform.
+ * @param sfi The session factory implementor where the Hibernate mappings can be found.
+ * @param parser A reference to the phase-1 parser
* @param tokenReplacements Registers the token replacement map with the walker. This map will
- * be used to substitute function names and constants.
- * @param collectionRole the role name of the collection used as the basis for the filter, NULL if this
+ * be used to substitute function names and constants.
+ * @param collectionRole The collection role name of the collection used as the basis for the
+ * filter, NULL if this is not a collection filter compilation.
*/
- public HqlSqlWalker(QueryTranslatorImpl qti,
- SessionFactoryImplementor sfi,
- HqlParser parser,
- Map tokenReplacements,
- String collectionRole) {
+ public HqlSqlWalker(
+ QueryTranslatorImpl qti,
+ SessionFactoryImplementor sfi,
+ HqlParser parser,
+ Map tokenReplacements,
+ String collectionRole) {
setASTFactory( new SqlASTFactory( this ) );
// Initialize the error handling delegate.
this.parseErrorHandler = new ErrorCounter();
@@ -186,37 +139,73 @@
this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
this.literalProcessor = new LiteralProcessor( this );
this.tokenReplacements = tokenReplacements;
- this.filterCollectionRole = collectionRole;
+ this.collectionFilterRole = collectionRole;
this.hqlParser = parser;
this.printer = new ASTPrinter( SqlTokenTypes.class );
}
protected void prepareFromClauseInputTree(AST fromClauseInput) {
- // Handle fiter compilation.
- // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
- if ( isFilter() && !isSubQuery() ) {
- QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
- Type collectionElementType = persister.getElementType();
- if ( !collectionElementType.isEntityType() ) {
- throw new QueryException( "collection of values in filter: this" );
- }
+ if ( !isSubQuery() ) {
+// // inject param specifications to account for dynamic filter param values
+// if ( ! getEnabledFilters().isEmpty() ) {
+// Iterator filterItr = getEnabledFilters().values().iterator();
+// while ( filterItr.hasNext() ) {
+// FilterImpl filter = ( FilterImpl ) filterItr.next();
+// if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) {
+// Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator();
+// while ( paramItr.hasNext() ) {
+// String parameterName = ( String ) paramItr.next();
+// // currently param filters *only* work with single-column parameter types;
+// // if that limitation is ever lifted, this logic will need to change to account for that
+// ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+// DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification(
+// filter.getName(),
+// parameterName,
+// filter.getFilterDefinition().getParameterType( parameterName ),
+// positionalParameterCount++
+// );
+// collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec );
+// parameters.add( paramSpec );
+// }
+// }
+// }
+// }
- String collectionElementEntityName = persister.getElementPersister().getEntityName();
- ASTFactory inputAstFactory = hqlParser.getASTFactory();
- AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
- ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
- fromClauseInput.addChild( fromElement );
- // Show the modified AST.
- if ( log.isDebugEnabled() ) {
- log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
+ if ( isFilter() ) {
+ // Handle collection-fiter compilation.
+ // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
+ QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
+ Type collectionElementType = persister.getElementType();
+ if ( !collectionElementType.isEntityType() ) {
+ throw new QueryException( "collection of values in filter: this" );
+ }
+
+ String collectionElementEntityName = persister.getElementPersister().getEntityName();
+ ASTFactory inputAstFactory = hqlParser.getASTFactory();
+ AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
+ ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
+ fromClauseInput.addChild( fromElement );
+ // Show the modified AST.
+ if ( log.isDebugEnabled() ) {
+ log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
+ }
+ queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
+
+ // Create a parameter specification for the collection filter...
+ Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ).getKeyType();
+ ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+ CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
+ collectionFilterRole, collectionFilterKeyType, positionalParameterCount++
+ );
+ collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
+ parameters.add( collectionFilterKeyParameterSpec );
}
- queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
}
}
- private boolean isFilter() {
- return filterCollectionRole != null;
+ public boolean isFilter() {
+ return collectionFilterRole != null;
}
public SessionFactoryHelper getSessionFactoryHelper() {
@@ -270,13 +259,13 @@
protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
FromClause fromClause = fromElement.getFromClause();
- QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
+ QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
// Get the names of the columns used to link between the collection
// owner and the collection elements.
String[] keyColumnNames = persister.getKeyColumnNames();
String fkTableAlias = persister.isOneToMany()
? fromElement.getTableAlias()
- : fromClause.getAliasGenerator().createName( filterCollectionRole );
+ : fromClause.getAliasGenerator().createName( collectionFilterRole );
JoinSequence join = sessionFactoryHelper.createJoinSequence();
join.setRoot( persister, fkTableAlias );
if ( !persister.isOneToMany() ) {
Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -87,10 +87,7 @@
final ParameterSpecification spec = ( ParameterSpecification ) parameterSpecifications.get( i );
if ( PositionalParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
PositionalParameterSpecification ordinalSpec = ( PositionalParameterSpecification ) spec;
- ordinalParameterList.add(
- ordinalSpec.getHqlPosition(),
- new ParameterInfo( i, ordinalSpec.getExpectedType() )
- );
+ ordinalParameterList.add( new ParameterInfo( i, ordinalSpec.getExpectedType() ) );
}
else if ( NamedParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
NamedParameterSpecification namedSpec = ( NamedParameterSpecification ) spec;
Modified: trunk/Hibernate3/src/org/hibernate/loader/Loader.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/loader/Loader.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/loader/Loader.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -1497,26 +1497,6 @@
}
/**
- * Bind positional parameter values to the <tt>PreparedStatement</tt>
- * (these are parameters specified by a JDBC-style ?).
- */
- protected int bindPositionalParameters(
- final PreparedStatement st,
- final QueryParameters queryParameters,
- final int start,
- final SessionImplementor session) throws SQLException, HibernateException {
-
- final Object[] values = queryParameters.getFilteredPositionalParameterValues();
- final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
- int span = 0;
- for ( int i = 0; i < values.length; i++ ) {
- types[i].nullSafeSet( st, values[i], start + span, session );
- span += types[i].getColumnSpan( getFactory() );
- }
- return span;
- }
-
- /**
* Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
* Bind JDBC-style <tt>?</tt> parameters, named parameters, and
* limit parameters.
@@ -1572,9 +1552,9 @@
if (callable) {
col = dialect.registerResultSetOutParameter( (CallableStatement)st, col );
}
- col += bindPositionalParameters( st, queryParameters, col, session );
- col += bindNamedParameters( st, queryParameters.getNamedParameters(), col, session );
+ col += bindParameterValues( st, queryParameters, col, session );
+
if ( useLimit && !dialect.bindLimitParametersFirst() ) {
col += bindLimitParameters( st, col, selection );
}
@@ -1646,7 +1626,78 @@
}
}
+ protected int bindParameterValues(
+ PreparedStatement st,
+ QueryParameters queryParameters,
+ int col,
+ SessionImplementor session) throws SQLException {
+ col += bindPositionalParameters( st, queryParameters, col, session );
+ col += bindNamedParameters( st, queryParameters.getNamedParameters(), col, session );
+ return col;
+ }
+
/**
+ * Bind positional parameter values to the <tt>PreparedStatement</tt>
+ * (these are parameters specified by a JDBC-style ?).
+ */
+ protected int bindPositionalParameters(
+ final PreparedStatement st,
+ final QueryParameters queryParameters,
+ final int start,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ final Object[] values = queryParameters.getFilteredPositionalParameterValues();
+ final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
+ int span = 0;
+ for ( int i = 0; i < values.length; i++ ) {
+ types[i].nullSafeSet( st, values[i], start + span, session );
+ span += types[i].getColumnSpan( getFactory() );
+ }
+ return span;
+ }
+
+ /**
+ * Bind named parameters to the <tt>PreparedStatement</tt>. This has an empty
+ * implementation on this superclass and should be implemented by subclasses
+ * (queries) which allow named parameters.
+ */
+ protected int bindNamedParameters(
+ final PreparedStatement ps,
+ final Map namedParams,
+ final int start,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ if ( namedParams != null ) {
+ // assumes that types are all of span 1
+ Iterator iter = namedParams.entrySet().iterator();
+ int result = 0;
+ while ( iter.hasNext() ) {
+ Map.Entry e = ( Map.Entry ) iter.next();
+ String name = ( String ) e.getKey();
+ TypedValue typedval = ( TypedValue ) e.getValue();
+ int[] locs = getNamedParameterLocs( name );
+ for ( int i = 0; i < locs.length; i++ ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "bindNamedParameters() " +
+ typedval.getValue() + " -> " + name +
+ " [" + ( locs[i] + start ) + "]"
+ );
+ }
+ typedval.getType().nullSafeSet( ps, typedval.getValue(), locs[i] + start, session );
+ }
+ result += locs.length;
+ }
+ return result;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ public int[] getNamedParameterLocs(String name) {
+ throw new AssertionFailure("no named parameters");
+ }
+
+ /**
* Fetch a <tt>PreparedStatement</tt>, call <tt>setMaxRows</tt> and then execute it,
* advance to the first result and return an SQL <tt>ResultSet</tt>
*/
@@ -1717,49 +1768,6 @@
}
/**
- * Bind named parameters to the <tt>PreparedStatement</tt>. This has an empty
- * implementation on this superclass and should be implemented by subclasses
- * (queries) which allow named parameters.
- */
- protected int bindNamedParameters(final PreparedStatement ps,
- final Map namedParams,
- final int start,
- final SessionImplementor session)
- throws SQLException, HibernateException {
-
- if ( namedParams != null ) {
- // assumes that types are all of span 1
- Iterator iter = namedParams.entrySet().iterator();
- int result = 0;
- while ( iter.hasNext() ) {
- Map.Entry e = ( Map.Entry ) iter.next();
- String name = ( String ) e.getKey();
- TypedValue typedval = ( TypedValue ) e.getValue();
- int[] locs = getNamedParameterLocs( name );
- for ( int i = 0; i < locs.length; i++ ) {
- if ( log.isDebugEnabled() ) {
- log.debug(
- "bindNamedParameters() " +
- typedval.getValue() + " -> " + name +
- " [" + ( locs[i] + start ) + "]"
- );
- }
- typedval.getType().nullSafeSet( ps, typedval.getValue(), locs[i] + start, session );
- }
- result += locs.length;
- }
- return result;
- }
- else {
- return 0;
- }
- }
-
- public int[] getNamedParameterLocs(String name) {
- throw new AssertionFailure("no named parameters");
- }
-
- /**
* Called by subclasses that load entities
* @param persister only needed for logging
*/
Modified: trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -9,6 +9,9 @@
import java.util.List;
import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.QueryException;
@@ -22,11 +25,12 @@
import org.hibernate.event.EventSource;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.hql.HolderInstantiator;
-import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
import org.hibernate.hql.ast.tree.FromElement;
import org.hibernate.hql.ast.tree.SelectClause;
import org.hibernate.impl.IteratorImpl;
import org.hibernate.loader.BasicLoader;
+import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable;
@@ -36,8 +40,6 @@
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.util.ArrayHelper;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
/**
* A delegate that implements the Loader part of QueryTranslator.
@@ -51,7 +53,7 @@
/**
* The query translator that is delegating to this object.
*/
- private QueryTranslator queryTranslator;
+ private QueryTranslatorImpl queryTranslator;
private Queryable[] entityPersisters;
private String[] entityAliases;
@@ -82,6 +84,7 @@
private LockMode[] defaultLockModes;
+ private final boolean isCollectionFilter;
@@ -89,14 +92,16 @@
* Creates a new Loader implementation.
*
* @param queryTranslator The query translator that is the delegator.
- * @param factory The factory from which this loader is being created.
+ * @param factory The factory from which this loader is being created.
+ * @param selectClause The AST representing the select clause for loading.
*/
public QueryLoader(
- final QueryTranslator queryTranslator,
+ final QueryTranslatorImpl queryTranslator,
final SessionFactoryImplementor factory,
final SelectClause selectClause) {
super( factory );
this.queryTranslator = queryTranslator;
+ this.isCollectionFilter = selectClause.getWalker().isFilter();
initialize( selectClause );
postInstantiate();
}
@@ -483,53 +488,44 @@
return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
}
- protected int bindNamedParameters(
- final PreparedStatement ps,
- final Map namedParams,
- final int start,
- final SessionImplementor session) throws SQLException, HibernateException {
- // we override binding of named parameters here because the parser
- // keeps track of named-parameter SQL positions "absolutely"; or at
- // least it thinks it does. But currently it does not know about filter
- // parameters. Thus we need to account for the number of filter-injected
- // parameters and adjust the bind positions for named parameters
- // accordingly...
- final int filterParamCountAdjustment = start - queryTranslator.getParameterTranslations().getOrdinalParameterCount();
- if ( namedParams != null ) {
- // assumes that types are all of span 1
- Iterator iter = namedParams.entrySet().iterator();
- int result = 0;
- while ( iter.hasNext() ) {
- Map.Entry e = ( Map.Entry ) iter.next();
- String name = ( String ) e.getKey();
- TypedValue typedval = ( TypedValue ) e.getValue();
- int[] locs = getNamedParameterLocs( name );
- for ( int i = 0; i < locs.length; i++ ) {
- final int location = locs[i] + filterParamCountAdjustment;
- if ( log.isDebugEnabled() ) {
- log.debug(
- "bindNamedParameters() " +
- typedval.getValue() + " -> " + name +
- " [" + location + "]"
- );
- }
- try {
- typedval.getType().nullSafeSet( ps, typedval.getValue(), location, session );
- }
- catch( ClassCastException cce ) {
- throw new TypeMismatchException(
- "named parameter [" + name + "] not of expected type; expected = " +
- typedval.getType().getReturnedClass() + "; but was =" +
- typedval.getValue().getClass().getName()
- );
- }
- }
- result += locs.length;
- }
- return result;
+ protected int bindParameterValues(
+ PreparedStatement st,
+ QueryParameters queryParameters,
+ int startPosition,
+ SessionImplementor session) throws SQLException {
+ int position = startPosition;
+ position = bindFilterParameterValues( st, queryParameters, position, session );
+ List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters();
+ Iterator itr = parameterSpecs.iterator();
+ while ( itr.hasNext() ) {
+ ParameterSpecification spec = ( ParameterSpecification ) itr.next();
+ position += spec.bind( st, queryParameters, session, position );
}
- else {
- return 0;
+ return position;
+ }
+
+ private int bindFilterParameterValues(
+ PreparedStatement st,
+ QueryParameters queryParameters,
+ int position,
+ SessionImplementor session) throws SQLException {
+ // todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification
+ // see the discussion there in DynamicFilterParameterSpecification's javadocs as to why
+ // it is currently not done that way.
+ int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null
+ ? 0
+ : queryParameters.getFilteredPositionalParameterTypes().length;
+ int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null
+ ? 0
+ : queryParameters.getPositionalParameterTypes().length;
+ int filterParamCount = filteredParamCount - nonfilteredParamCount;
+ for ( int i = 0; i < filterParamCount; i++ ) {
+ Type type = queryParameters.getFilteredPositionalParameterTypes()[i];
+ Object value = queryParameters.getFilteredPositionalParameterValues()[i];
+ type.nullSafeSet( st, value, position, session );
+ position += type.getColumnSpan( getFactory() );
}
+
+ return position;
}
}
Added: trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -0,0 +1,57 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a collection-key
+ * as part of a collection filter compilation.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionFilterKeyParameterSpecification implements ParameterSpecification {
+
+ private final String collectionRole;
+ private final Type keyType;
+ private final int queryParameterPosition;
+
+ /**
+ * Creates a specialized collection-filter collection-key parameter spec.
+ *
+ * @param collectionRole The collection role being filtered.
+ * @param keyType The mapped collection-key type.
+ * @param queryParameterPosition The position within {@link org.hibernate.engine.QueryParameters} where
+ * we can find the appropriate param value to bind.
+ */
+ public CollectionFilterKeyParameterSpecification(String collectionRole, Type keyType, int queryParameterPosition) {
+ this.collectionRole = collectionRole;
+ this.keyType = keyType;
+ this.queryParameterPosition = queryParameterPosition;
+ }
+
+ public int bind(
+ PreparedStatement statement,
+ QueryParameters qp,
+ SessionImplementor session,
+ int position) throws SQLException {
+ Object value = qp.getPositionalParameterValues()[queryParameterPosition];
+ keyType.nullSafeSet( statement, value, position, session );
+ return keyType.getColumnSpan( session.getFactory() );
+ }
+
+ public Type getExpectedType() {
+ return keyType;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ // todo : throw exception?
+ }
+
+ public String renderDisplayInfo() {
+ return "collection-filter-key=" + collectionRole;
+ }
+}
Added: trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -0,0 +1,60 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a dynamic filter
+ * parameters.
+ * <p/>
+ * Note: this class is currently not used. The ideal way to deal with dynamic filter
+ * parameters for HQL would be to track them just as we do with other parameters
+ * in the translator. However, the problem with that is that we currently do not
+ * know the filters which actually apply to the query; we know the active/enabled ones,
+ * but not the ones that actually "make it into" the resulting query.
+ *
+ * @author Steve Ebersole
+ */
+public class DynamicFilterParameterSpecification implements ParameterSpecification {
+ private final String filterName;
+ private final String parameterName;
+ private final Type definedParameterType;
+ private final int queryParameterPosition;
+
+ public DynamicFilterParameterSpecification(
+ String filterName,
+ String parameterName,
+ Type definedParameterType,
+ int queryParameterPosition) {
+ this.filterName = filterName;
+ this.parameterName = parameterName;
+ this.definedParameterType = definedParameterType;
+ this.queryParameterPosition = queryParameterPosition;
+ }
+
+ public int bind(
+ PreparedStatement statement,
+ QueryParameters qp,
+ SessionImplementor session,
+ int position) throws SQLException {
+ Object value = qp.getFilteredPositionalParameterValues()[queryParameterPosition];
+ definedParameterType.nullSafeSet( statement, value, position, session );
+ return definedParameterType.getColumnSpan( session.getFactory() );
+ }
+
+ public Type getExpectedType() {
+ return definedParameterType;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ // todo : throw exception?
+ }
+
+ public String renderDisplayInfo() {
+ return "dynamic-filter={filterName=" + filterName + ",paramName=" + parameterName + "}";
+ }
+}
Modified: trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-10-19 03:27:04 UTC (rev 10615)
@@ -198,6 +198,29 @@
s.close();
}
+ public void testComponentParameterBinding() {
+ // HHH-1774 : parameters are bound incorrectly with component parameters...
+ Session s = openSession();
+ s.beginTransaction();
+
+ Order.Id oId = new Order.Id( "1234", 1 );
+
+ // control
+ s.createQuery("from Order o where o.customer.name =:name and o.id = :id")
+ .setParameter( "name", "oracle" )
+ .setParameter( "id", oId )
+ .list();
+
+ // this is the form that caused problems in the original case...
+ s.createQuery("from Order o where o.id = :id and o.customer.name =:name ")
+ .setParameter( "id", oId )
+ .setParameter( "name", "oracle" )
+ .list();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
public void testAnyMappingReference() {
Session s = openSession();
s.beginTransaction();
17 years, 6 months
Hibernate SVN: r10614 - in branches/Branch_3_2/Hibernate3: src/org/hibernate/action src/org/hibernate/hql/ast src/org/hibernate/loader src/org/hibernate/loader/hql src/org/hibernate/param test/org/hibernate/test/hql
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-18 23:25:41 -0400 (Wed, 18 Oct 2006)
New Revision: 10614
Added:
branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
Log:
HHH-1774 : proper binding of multi-column parameter types to queries
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -99,41 +99,35 @@
instance,
rowId,
session
- );
+ );
}
-
EntityEntry entry = getSession().getPersistenceContext().getEntry( instance );
if ( entry == null ) {
throw new AssertionFailure( "possible nonthreadsafe access to session" );
}
-
- if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
- // get the updated snapshot by cloning current state
- // it is safe to copy in place, since by this time
- // no-one else has a reference to the array
- TypeFactory.deepCopy(
- state,
- persister.getPropertyTypes(),
- persister.getPropertyCheckability(),
- state,
- session
- );
-
+ if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
if ( persister.hasUpdateGeneratedProperties() ) {
+ // get the updated snapshot by cloning current state;
+ // it is safe to copy in place, since by this time
+ // no-one else has a reference to the array
+ TypeFactory.deepCopy(
+ state,
+ persister.getPropertyTypes(),
+ persister.getPropertyCheckability(),
+ state,
+ session
+ );
persister.processUpdateGeneratedProperties( id, instance, state, session );
if ( persister.isVersionPropertyGenerated() ) {
nextVersion = Versioning.getVersion(state, persister);
}
}
-
entry.postUpdate( instance, state, nextVersion );
-
}
if ( persister.hasCache() ) {
-
if ( persister.isCacheInvalidationRequired() || entry.getStatus()!=Status.MANAGED ) {
persister.getCache().evict(ck);
}
@@ -146,7 +140,7 @@
nextVersion,
getSession(),
instance
- );
+ );
cacheEntry = persister.getCacheEntryStructure().structure(ce);
// boolean put = persister.getCache().update(ck, cacheEntry);
boolean put = persister.getCache().update( ck, cacheEntry, nextVersion, previousVersion );
@@ -156,7 +150,6 @@
.secondLevelCachePut( getPersister().getCache().getRegionName() );
}
}
-
}
postUpdate();
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -15,6 +15,7 @@
import org.apache.commons.logging.LogFactory;
import org.hibernate.QueryException;
import org.hibernate.HibernateException;
+import org.hibernate.impl.FilterImpl;
import org.hibernate.engine.JoinSequence;
import org.hibernate.engine.ParameterBinder;
import org.hibernate.engine.SessionFactoryImplementor;
@@ -60,6 +61,8 @@
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.param.VersionTypeSeedParameterSpecification;
+import org.hibernate.param.CollectionFilterKeyParameterSpecification;
+import org.hibernate.param.DynamicFilterParameterSpecification;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
@@ -87,136 +90,125 @@
* @see SqlASTFactory
*/
public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
- private static Log log = LogFactory.getLog( HqlSqlWalker.class );
+ private static final Log log = LogFactory.getLog( HqlSqlWalker.class );
- /**
- * A delegate that handles the Hiberanate meta data model.
- */
- private SessionFactoryHelper sessionFactoryHelper;
+ private final QueryTranslatorImpl queryTranslatorImpl;
+ private final HqlParser hqlParser;
+ private final SessionFactoryHelper sessionFactoryHelper;
+ private final Map tokenReplacements;
+ private final AliasGenerator aliasGenerator = new AliasGenerator();
+ private final LiteralProcessor literalProcessor;
+ private final ParseErrorHandler parseErrorHandler;
+ private final ASTPrinter printer;
+ private final String collectionFilterRole;
- /**
- * A delegate that handles literal constants.
- */
- private LiteralProcessor literalProcessor;
-
- /**
- * Handles parser errors.
- */
- private ParseErrorHandler parseErrorHandler;
-
- /**
- * The current context.
- */
private FromClause currentFromClause = null;
-
- /**
- * The top-level SelectClause.
- */
private SelectClause selectClause;
- /**
- * Generates alias names for tables.
- */
- private AliasGenerator aliasGenerator = new AliasGenerator();
-
- /**
- * The set of unique query spaces (a.k.a. table names).
- */
private Set querySpaces = new HashSet();
- /**
- * A (string->string) map is used to substitute function names and literals.
- */
- private Map tokenReplacements;
-
- private QueryTranslatorImpl queryTranslatorImpl;
-
- /**
- * The number of parameters encountered so far.
- */
private int parameterCount;
-
- /**
- * A map of lists which associates named and numbered parameters to a list of occurrences in the query.
- */
private Map namedParameters = new HashMap();
-
- /**
- * The filter collection role, or null if this isn't a filter compilation.
- */
- private String filterCollectionRole;
-
- /**
- * The parser that performed phase 1 - parse the HQL into an HQL tree.
- */
- private HqlParser hqlParser;
-
- private ASTPrinter printer;
-
- /**
- * The join type for any implied joins.
- */
- private int impliedJoinType;
-
private ArrayList parameters = new ArrayList();
private int numberOfParametersInSetClause;
private int positionalParameterCount;
private ArrayList assignmentSpecifications = new ArrayList();
+ private int impliedJoinType;
+
+
+
/**
* Create a new tree transformer.
*
- * @param qti Back pointer to the query translator implementation that is using this tree transform.
- * @param sfi The session factory implementor where the Hibernate mappings can be found.
- * @param parser
+ * @param qti Back pointer to the query translator implementation that is using this tree transform.
+ * @param sfi The session factory implementor where the Hibernate mappings can be found.
+ * @param parser A reference to the phase-1 parser
* @param tokenReplacements Registers the token replacement map with the walker. This map will
- * be used to substitute function names and constants.
- * @param collectionRole the role name of the collection used as the basis for the filter, NULL if this
+ * be used to substitute function names and constants.
+ * @param collectionRole The collection role name of the collection used as the basis for the
+ * filter, NULL if this is not a collection filter compilation.
*/
- public HqlSqlWalker(QueryTranslatorImpl qti,
- SessionFactoryImplementor sfi,
- HqlParser parser,
- Map tokenReplacements,
- String collectionRole) {
+ public HqlSqlWalker(
+ QueryTranslatorImpl qti,
+ SessionFactoryImplementor sfi,
+ HqlParser parser,
+ Map tokenReplacements,
+ String collectionRole) {
setASTFactory( new SqlASTFactory( this ) );
- // Initialize the error handling delegate.
this.parseErrorHandler = new ErrorCounter();
this.queryTranslatorImpl = qti;
this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
this.literalProcessor = new LiteralProcessor( this );
this.tokenReplacements = tokenReplacements;
- this.filterCollectionRole = collectionRole;
this.hqlParser = parser;
this.printer = new ASTPrinter( SqlTokenTypes.class );
+ this.collectionFilterRole = collectionRole;
}
protected void prepareFromClauseInputTree(AST fromClauseInput) {
- // Handle fiter compilation.
- // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
- if ( isFilter() && !isSubQuery() ) {
- QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
- Type collectionElementType = persister.getElementType();
- if ( !collectionElementType.isEntityType() ) {
- throw new QueryException( "collection of values in filter: this" );
- }
+ if ( !isSubQuery() ) {
+// // inject param specifications to account for dynamic filter param values
+// if ( ! getEnabledFilters().isEmpty() ) {
+// Iterator filterItr = getEnabledFilters().values().iterator();
+// while ( filterItr.hasNext() ) {
+// FilterImpl filter = ( FilterImpl ) filterItr.next();
+// if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) {
+// Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator();
+// while ( paramItr.hasNext() ) {
+// String parameterName = ( String ) paramItr.next();
+// // currently param filters *only* work with single-column parameter types;
+// // if that limitation is ever lifted, this logic will need to change to account for that
+// ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+// DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification(
+// filter.getName(),
+// parameterName,
+// filter.getFilterDefinition().getParameterType( parameterName ),
+// positionalParameterCount++
+// );
+// collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec );
+// parameters.add( paramSpec );
+// }
+// }
+// }
+// }
- String collectionElementEntityName = persister.getElementPersister().getEntityName();
- ASTFactory inputAstFactory = hqlParser.getASTFactory();
- AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
- ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
- fromClauseInput.addChild( fromElement );
- // Show the modified AST.
- if ( log.isDebugEnabled() ) {
- log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
+ if ( isFilter() ) {
+ // Handle collection-fiter compilation.
+ // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
+ QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
+ Type collectionElementType = persister.getElementType();
+ if ( !collectionElementType.isEntityType() ) {
+ throw new QueryException( "collection of values in filter: this" );
+ }
+
+ String collectionElementEntityName = persister.getElementPersister().getEntityName();
+ ASTFactory inputAstFactory = hqlParser.getASTFactory();
+ AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
+ ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
+ fromClauseInput.addChild( fromElement );
+ // Show the modified AST.
+ if ( log.isDebugEnabled() ) {
+ log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
+ }
+ queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
+
+ // Create a parameter specification for the collection filter...
+ Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ).getKeyType();
+ ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+ CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
+ collectionFilterRole, collectionFilterKeyType, positionalParameterCount++
+ );
+ collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
+ parameters.add( collectionFilterKeyParameterSpec );
}
- queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
}
}
- private boolean isFilter() {
- return filterCollectionRole != null;
+ public boolean isFilter() {
+ return collectionFilterRole != null;
}
public SessionFactoryHelper getSessionFactoryHelper() {
@@ -270,13 +262,13 @@
protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
FromClause fromClause = fromElement.getFromClause();
- QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
+ QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
// Get the names of the columns used to link between the collection
// owner and the collection elements.
String[] keyColumnNames = persister.getKeyColumnNames();
String fkTableAlias = persister.isOneToMany()
? fromElement.getTableAlias()
- : fromClause.getAliasGenerator().createName( filterCollectionRole );
+ : fromClause.getAliasGenerator().createName( collectionFilterRole );
JoinSequence join = sessionFactoryHelper.createJoinSequence();
join.setRoot( persister, fkTableAlias );
if ( !persister.isOneToMany() ) {
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -19,7 +19,7 @@
* Defines the information available for parameters encountered during
* query translation through the antlr-based parser.
*
- * @author <a href="mailto:steve@hibernate.org">Steve Ebersole </a>
+ * @author Steve Ebersole
*/
public class ParameterTranslationsImpl implements ParameterTranslations {
@@ -87,10 +87,7 @@
final ParameterSpecification spec = ( ParameterSpecification ) parameterSpecifications.get( i );
if ( PositionalParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
PositionalParameterSpecification ordinalSpec = ( PositionalParameterSpecification ) spec;
- ordinalParameterList.add(
- ordinalSpec.getHqlPosition(),
- new ParameterInfo( i, ordinalSpec.getExpectedType() )
- );
+ ordinalParameterList.add( new ParameterInfo( i, ordinalSpec.getExpectedType() ) );
}
else if ( NamedParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
NamedParameterSpecification namedSpec = ( NamedParameterSpecification ) spec;
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -1497,26 +1497,6 @@
}
/**
- * Bind positional parameter values to the <tt>PreparedStatement</tt>
- * (these are parameters specified by a JDBC-style ?).
- */
- protected int bindPositionalParameters(
- final PreparedStatement st,
- final QueryParameters queryParameters,
- final int start,
- final SessionImplementor session) throws SQLException, HibernateException {
-
- final Object[] values = queryParameters.getFilteredPositionalParameterValues();
- final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
- int span = 0;
- for ( int i = 0; i < values.length; i++ ) {
- types[i].nullSafeSet( st, values[i], start + span, session );
- span += types[i].getColumnSpan( getFactory() );
- }
- return span;
- }
-
- /**
* Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
* Bind JDBC-style <tt>?</tt> parameters, named parameters, and
* limit parameters.
@@ -1525,9 +1505,7 @@
final QueryParameters queryParameters,
final boolean scroll,
final SessionImplementor session) throws SQLException, HibernateException {
-
- queryParameters.processFilters( getSQLString(), session );
- String sql = queryParameters.getFilteredSQL();
+ String sql = processFilters( queryParameters, session );
final Dialect dialect = getFactory().getDialect();
final RowSelection selection = queryParameters.getRowSelection();
boolean useLimit = useLimit( selection, dialect );
@@ -1572,9 +1550,9 @@
if (callable) {
col = dialect.registerResultSetOutParameter( (CallableStatement)st, col );
}
- col += bindPositionalParameters( st, queryParameters, col, session );
- col += bindNamedParameters( st, queryParameters.getNamedParameters(), col, session );
+ col += bindParameterValues( st, queryParameters, col, session );
+
if ( useLimit && !dialect.bindLimitParametersFirst() ) {
col += bindLimitParameters( st, col, selection );
}
@@ -1601,6 +1579,11 @@
return st;
}
+ protected String processFilters(QueryParameters queryParameters, SessionImplementor session) {
+ queryParameters.processFilters( getSQLString(), session );
+ return queryParameters.getFilteredSQL();
+ }
+
/**
* Some dialect-specific LIMIT clauses require the maximium last row number,
* others require the maximum returned row count.
@@ -1646,7 +1629,80 @@
}
}
+ protected int bindParameterValues(
+ PreparedStatement st,
+ QueryParameters queryParameters,
+ int col,
+ SessionImplementor session) throws SQLException {
+ col += bindPositionalParameters( st, queryParameters, col, session );
+ col += bindNamedParameters( st, queryParameters.getNamedParameters(), col, session );
+ return col;
+ }
+
/**
+ * Bind positional parameter values to the <tt>PreparedStatement</tt>
+ * (these are parameters specified by a JDBC-style ?).
+ */
+ protected int bindPositionalParameters(
+ final PreparedStatement st,
+ final QueryParameters queryParameters,
+ final int start,
+ final SessionImplementor session) throws SQLException, HibernateException {
+
+ final Object[] values = queryParameters.getFilteredPositionalParameterValues();
+ final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
+ int span = 0;
+ for ( int i = 0; i < values.length; i++ ) {
+ types[i].nullSafeSet( st, values[i], start + span, session );
+ span += types[i].getColumnSpan( getFactory() );
+ }
+ return span;
+ }
+
+ /**
+ * Bind named parameters to the <tt>PreparedStatement</tt>. This has an empty
+ * implementation on this superclass and should be implemented by subclasses
+ * (queries) which allow named parameters.
+ */
+ protected int bindNamedParameters(final PreparedStatement ps,
+ final Map namedParams,
+ final int start,
+ final SessionImplementor session)
+ throws SQLException, HibernateException {
+
+ if ( namedParams != null ) {
+ // assumes that types are all of span 1
+ Iterator iter = namedParams.entrySet().iterator();
+ int result = 0;
+ while ( iter.hasNext() ) {
+ Map.Entry e = ( Map.Entry ) iter.next();
+ String name = ( String ) e.getKey();
+ TypedValue typedval = ( TypedValue ) e.getValue();
+ int[] locs = getNamedParameterLocs( name );
+ for ( int i = 0; i < locs.length; i++ ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "bindNamedParameters() " +
+ typedval.getValue() + " -> " + name +
+ " [" + ( locs[i] + start ) + "]"
+ );
+ }
+ typedval.getType().nullSafeSet( ps, typedval.getValue(), locs[i] + start, session );
+ }
+ result += locs.length;
+ }
+ return result;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ public int[] getNamedParameterLocs(String name) {
+ throw new AssertionFailure("no named parameters");
+ }
+
+ /**
* Fetch a <tt>PreparedStatement</tt>, call <tt>setMaxRows</tt> and then execute it,
* advance to the first result and return an SQL <tt>ResultSet</tt>
*/
@@ -1717,49 +1773,6 @@
}
/**
- * Bind named parameters to the <tt>PreparedStatement</tt>. This has an empty
- * implementation on this superclass and should be implemented by subclasses
- * (queries) which allow named parameters.
- */
- protected int bindNamedParameters(final PreparedStatement ps,
- final Map namedParams,
- final int start,
- final SessionImplementor session)
- throws SQLException, HibernateException {
-
- if ( namedParams != null ) {
- // assumes that types are all of span 1
- Iterator iter = namedParams.entrySet().iterator();
- int result = 0;
- while ( iter.hasNext() ) {
- Map.Entry e = ( Map.Entry ) iter.next();
- String name = ( String ) e.getKey();
- TypedValue typedval = ( TypedValue ) e.getValue();
- int[] locs = getNamedParameterLocs( name );
- for ( int i = 0; i < locs.length; i++ ) {
- if ( log.isDebugEnabled() ) {
- log.debug(
- "bindNamedParameters() " +
- typedval.getValue() + " -> " + name +
- " [" + ( locs[i] + start ) + "]"
- );
- }
- typedval.getType().nullSafeSet( ps, typedval.getValue(), locs[i] + start, session );
- }
- result += locs.length;
- }
- return result;
- }
- else {
- return 0;
- }
- }
-
- public int[] getNamedParameterLocs(String name) {
- throw new AssertionFailure("no named parameters");
- }
-
- /**
* Called by subclasses that load entities
* @param persister only needed for logging
*/
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -9,24 +9,26 @@
import java.util.List;
import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.QueryException;
import org.hibernate.ScrollableResults;
-import org.hibernate.TypeMismatchException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
-import org.hibernate.engine.TypedValue;
import org.hibernate.event.EventSource;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.hql.HolderInstantiator;
-import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
import org.hibernate.hql.ast.tree.FromElement;
import org.hibernate.hql.ast.tree.SelectClause;
import org.hibernate.impl.IteratorImpl;
import org.hibernate.loader.BasicLoader;
+import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable;
@@ -36,8 +38,6 @@
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.util.ArrayHelper;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
/**
* A delegate that implements the Loader part of QueryTranslator.
@@ -48,10 +48,8 @@
private static final Log log = LogFactory.getLog( QueryLoader.class );
- /**
- * The query translator that is delegating to this object.
- */
- private QueryTranslator queryTranslator;
+ private final QueryTranslatorImpl queryTranslator;
+ private final boolean isCollectionFilter;
private Queryable[] entityPersisters;
private String[] entityAliases;
@@ -63,7 +61,6 @@
private boolean hasScalars;
private String[][] scalarColumnNames;
- //private Type[] sqlResultTypes;
private Type[] queryReturnTypes;
private final Map sqlAliasByEntityAlias = new HashMap(8);
@@ -83,28 +80,26 @@
private LockMode[] defaultLockModes;
-
-
/**
* Creates a new Loader implementation.
*
* @param queryTranslator The query translator that is the delegator.
- * @param factory The factory from which this loader is being created.
+ * @param factory The factory from which this loader is being created.
+ * @param selectClause The AST representing the select clause for loading.
*/
public QueryLoader(
- final QueryTranslator queryTranslator,
+ final QueryTranslatorImpl queryTranslator,
final SessionFactoryImplementor factory,
final SelectClause selectClause) {
super( factory );
this.queryTranslator = queryTranslator;
+ this.isCollectionFilter = selectClause.getWalker().isFilter();
initialize( selectClause );
postInstantiate();
}
private void initialize(SelectClause selectClause) {
-
List fromElementList = selectClause.getFromElementsForLoad();
-
hasScalars = selectClause.isScalarSelect();
scalarColumnNames = selectClause.getColumnNames();
//sqlResultTypes = selectClause.getSqlResultTypes();
@@ -483,53 +478,45 @@
return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
}
- protected int bindNamedParameters(
- final PreparedStatement ps,
- final Map namedParams,
- final int start,
- final SessionImplementor session) throws SQLException, HibernateException {
- // we override binding of named parameters here because the parser
- // keeps track of named-parameter SQL positions "absolutely"; or at
- // least it thinks it does. But currently it does not know about filter
- // parameters. Thus we need to account for the number of filter-injected
- // parameters and adjust the bind positions for named parameters
- // accordingly...
- final int filterParamCountAdjustment = start - queryTranslator.getParameterTranslations().getOrdinalParameterCount();
- if ( namedParams != null ) {
- // assumes that types are all of span 1
- Iterator iter = namedParams.entrySet().iterator();
- int result = 0;
- while ( iter.hasNext() ) {
- Map.Entry e = ( Map.Entry ) iter.next();
- String name = ( String ) e.getKey();
- TypedValue typedval = ( TypedValue ) e.getValue();
- int[] locs = getNamedParameterLocs( name );
- for ( int i = 0; i < locs.length; i++ ) {
- final int location = locs[i] + filterParamCountAdjustment;
- if ( log.isDebugEnabled() ) {
- log.debug(
- "bindNamedParameters() " +
- typedval.getValue() + " -> " + name +
- " [" + location + "]"
- );
- }
- try {
- typedval.getType().nullSafeSet( ps, typedval.getValue(), location, session );
- }
- catch( ClassCastException cce ) {
- throw new TypeMismatchException(
- "named parameter [" + name + "] not of expected type; expected = " +
- typedval.getType().getReturnedClass() + "; but was =" +
- typedval.getValue().getClass().getName()
- );
- }
- }
- result += locs.length;
- }
- return result;
+
+ protected int bindParameterValues(
+ PreparedStatement st,
+ QueryParameters queryParameters,
+ int startPosition,
+ SessionImplementor session) throws SQLException {
+ int position = startPosition;
+ position = bindFilterParameterValues( st, queryParameters, position, session );
+ List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters();
+ Iterator itr = parameterSpecs.iterator();
+ while ( itr.hasNext() ) {
+ ParameterSpecification spec = ( ParameterSpecification ) itr.next();
+ position += spec.bind( st, queryParameters, session, position );
}
- else {
- return 0;
+ return position;
+ }
+
+ private int bindFilterParameterValues(
+ PreparedStatement st,
+ QueryParameters queryParameters,
+ int position,
+ SessionImplementor session) throws SQLException {
+ // todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification
+ // see the discussion there in DynamicFilterParameterSpecification's javadocs as to why
+ // it is currently not done that way.
+ int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null
+ ? 0
+ : queryParameters.getFilteredPositionalParameterTypes().length;
+ int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null
+ ? 0
+ : queryParameters.getPositionalParameterTypes().length;
+ int filterParamCount = filteredParamCount - nonfilteredParamCount;
+ for ( int i = 0; i < filterParamCount; i++ ) {
+ Type type = queryParameters.getFilteredPositionalParameterTypes()[i];
+ Object value = queryParameters.getFilteredPositionalParameterValues()[i];
+ type.nullSafeSet( st, value, position, session );
+ position += type.getColumnSpan( getFactory() );
}
+
+ return position;
}
}
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -0,0 +1,58 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a collection-key
+ * as part of a collection filter compilation.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionFilterKeyParameterSpecification implements ParameterSpecification {
+
+ private final String collectionRole;
+ private final Type keyType;
+ private final int queryParameterPosition;
+
+
+ /**
+ * Creates a specialized collection-filter collection-key parameter spec.
+ *
+ * @param collectionRole The collection role being filtered.
+ * @param keyType The mapped collection-key type.
+ * @param queryParameterPosition The position within {@link org.hibernate.engine.QueryParameters} where
+ * we can find the appropriate param value to bind.
+ */
+ public CollectionFilterKeyParameterSpecification(String collectionRole, Type keyType, int queryParameterPosition) {
+ this.collectionRole = collectionRole;
+ this.keyType = keyType;
+ this.queryParameterPosition = queryParameterPosition;
+ }
+
+ public int bind(
+ PreparedStatement statement,
+ QueryParameters qp,
+ SessionImplementor session,
+ int position) throws SQLException {
+ Object value = qp.getPositionalParameterValues()[queryParameterPosition];
+ keyType.nullSafeSet( statement, value, position, session );
+ return keyType.getColumnSpan( session.getFactory() );
+ }
+
+ public Type getExpectedType() {
+ return keyType;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ // todo : throw exception?
+ }
+
+ public String renderDisplayInfo() {
+ return "collection-filter-key=" + collectionRole;
+ }
+}
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -0,0 +1,60 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a dynamic filter
+ * parameters.
+ * <p/>
+ * Note: this class is currently not used. The ideal way to deal with dynamic filter
+ * parameters for HQL would be to track them just as we do with other parameters
+ * in the translator. However, the problem with that is that we currently do not
+ * know the filters which actually apply to the query; we know the active/enabled ones,
+ * but not the ones that actually "make it into" the resulting query.
+ *
+ * @author Steve Ebersole
+ */
+public class DynamicFilterParameterSpecification implements ParameterSpecification {
+ private final String filterName;
+ private final String parameterName;
+ private final Type definedParameterType;
+ private final int queryParameterPosition;
+
+ public DynamicFilterParameterSpecification(
+ String filterName,
+ String parameterName,
+ Type definedParameterType,
+ int queryParameterPosition) {
+ this.filterName = filterName;
+ this.parameterName = parameterName;
+ this.definedParameterType = definedParameterType;
+ this.queryParameterPosition = queryParameterPosition;
+ }
+
+ public int bind(
+ PreparedStatement statement,
+ QueryParameters qp,
+ SessionImplementor session,
+ int position) throws SQLException {
+ Object value = qp.getFilteredPositionalParameterValues()[queryParameterPosition];
+ definedParameterType.nullSafeSet( statement, value, position, session );
+ return definedParameterType.getColumnSpan( session.getFactory() );
+ }
+
+ public Type getExpectedType() {
+ return definedParameterType;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ // todo : throw exception?
+ }
+
+ public String renderDisplayInfo() {
+ return "dynamic-filter={filterName=" + filterName + ",paramName=" + parameterName + "}";
+ }
+}
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -125,6 +125,29 @@
s.close();
}
+ public void testComponentParameterBinding() {
+ // HHH-1774 : parameters are bound incorrectly with component parameters...
+ Session s = openSession();
+ s.beginTransaction();
+
+ Order.Id oId = new Order.Id( "1234", 1 );
+
+ // control
+ s.createQuery("from Order o where o.customer.name =:name and o.id = :id")
+ .setParameter( "name", "oracle" )
+ .setParameter( "id", oId )
+ .list();
+
+ // this is the form that caused problems in the original case...
+ s.createQuery("from Order o where o.id = :id and o.customer.name =:name ")
+ .setParameter( "id", oId )
+ .setParameter( "name", "oracle" )
+ .list();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
public void testAnyMappingReference() {
Session s = openSession();
s.beginTransaction();
17 years, 6 months
Hibernate SVN: r10613 - trunk/Hibernate3/test/org/hibernate/test/subclassfilter
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-18 21:44:38 -0400 (Wed, 18 Oct 2006)
New Revision: 10613
Modified:
trunk/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java
trunk/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java
Log:
fixed test issue on HSQLDB
Modified: trunk/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java 2006-10-19 01:42:50 UTC (rev 10612)
+++ trunk/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java 2006-10-19 01:44:38 UTC (rev 10613)
@@ -79,6 +79,8 @@
s = openSession();
t = s.beginTransaction();
+ s.createQuery( "delete Customer where contactOwner is not null" ).executeUpdate();
+ s.createQuery( "delete Employee where manager is not null" ).executeUpdate();
s.createQuery( "delete Person" ).executeUpdate();
t.commit();
s.close();
Modified: trunk/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java 2006-10-19 01:42:50 UTC (rev 10612)
+++ trunk/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java 2006-10-19 01:44:38 UTC (rev 10613)
@@ -90,6 +90,8 @@
s = openSession();
t = s.beginTransaction();
+ s.createQuery( "delete Customer where contactOwner is not null" ).executeUpdate();
+ s.createQuery( "delete Employee where manager is not null" ).executeUpdate();
s.createQuery( "delete Person" ).executeUpdate();
t.commit();
s.close();
17 years, 6 months
Hibernate SVN: r10612 - branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-10-18 21:42:50 -0400 (Wed, 18 Oct 2006)
New Revision: 10612
Modified:
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java
Log:
fixed test issue on HSQLDB
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java 2006-10-18 22:09:34 UTC (rev 10611)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java 2006-10-19 01:42:50 UTC (rev 10612)
@@ -79,6 +79,8 @@
s = openSession();
t = s.beginTransaction();
+ s.createQuery( "delete Customer where contactOwner is not null" ).executeUpdate();
+ s.createQuery( "delete Employee where manager is not null" ).executeUpdate();
s.createQuery( "delete Person" ).executeUpdate();
t.commit();
s.close();
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java 2006-10-18 22:09:34 UTC (rev 10611)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java 2006-10-19 01:42:50 UTC (rev 10612)
@@ -90,6 +90,8 @@
s = openSession();
t = s.beginTransaction();
+ s.createQuery( "delete Customer where contactOwner is not null" ).executeUpdate();
+ s.createQuery( "delete Employee where manager is not null" ).executeUpdate();
s.createQuery( "delete Person" ).executeUpdate();
t.commit();
s.close();
17 years, 6 months
Hibernate SVN: r10611 - in branches/Lucene_Integration/HibernateExt/metadata/src: java/org/hibernate/lucene java/org/hibernate/lucene/bridge test/org/hibernate/lucene/test
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2006-10-18 18:09:34 -0400 (Wed, 18 Oct 2006)
New Revision: 10611
Added:
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentId.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Field.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Index.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Store.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IdFieldBridge.java
Modified:
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Keyword.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Text.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Unstored.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java
branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java
branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java
Log:
ANN-383 adjust to the Lucene 2 APIs (deprecate the old annotations)
Use of idBridge to remove an element from the index
ANN-468 IdFieldBridge (but the String2FieldBridgeAdaptor is broken
Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -18,6 +18,7 @@
import org.hibernate.cfg.annotations.Version;
import org.hibernate.lucene.bridge.BridgeFactory;
import org.hibernate.lucene.bridge.FieldBridge;
+import org.hibernate.lucene.bridge.IdFieldBridge;
import org.hibernate.lucene.event.LuceneEventListener;
import org.hibernate.lucene.store.DirectoryProvider;
import org.hibernate.lucene.util.BinderHelper;
@@ -51,6 +52,11 @@
private final List<XMember> textGetters = new ArrayList<XMember>();
private final List<String> textNames = new ArrayList<String>();
private final List<FieldBridge> textBridges = new ArrayList<FieldBridge>();
+ private final List<String> fieldNames = new ArrayList<String>();
+ private final List<XMember> fieldGetters = new ArrayList<XMember>();
+ private final List<FieldBridge> fieldBridges = new ArrayList<FieldBridge>();
+ private final List<Field.Store> fieldStore = new ArrayList<Field.Store>();
+ private final List<Field.Index> fieldIndex = new ArrayList<Field.Index>();
private final XClass beanClass;
private final DirectoryProvider directoryProvider;
@@ -58,10 +64,11 @@
private final Analyzer analyzer;
private Float idBoost;
public static final String CLASS_FIELDNAME = "_hibernate_class";
- private FieldBridge idBridge;
+ private IdFieldBridge idBridge;
private Set<Class> mappedSubclasses = new HashSet<Class>();
private ReflectionManager reflectionManager;
+
public DocumentBuilder(XClass clazz, Analyzer analyzer, DirectoryProvider directory, ReflectionManager reflectionManager) {
this.beanClass = clazz;
this.analyzer = analyzer;
@@ -85,7 +92,7 @@
}
if ( idKeywordName == null ) {
- throw new HibernateException( "No id Keyword for: " + clazz.getName() );
+ throw new HibernateException( "No document id for: " + clazz.getName() );
}
}
@@ -96,7 +103,13 @@
if ( keywordAnn.id() ) {
idKeywordName = name;
idBoost = getBoost( member );
- idBridge = BridgeFactory.guessType( member );
+ FieldBridge fieldBridge = BridgeFactory.guessType( member );
+ if ( fieldBridge instanceof IdFieldBridge ) {
+ idBridge = (IdFieldBridge) fieldBridge;
+ }
+ else {
+ throw new HibernateException( "Bridge for document id does not implement IdFieldBridge: " + member.getName() );
+ }
}
else {
setAccessible( member );
@@ -121,9 +134,63 @@
textNames.add( BinderHelper.getAttributeName( member, textAnn.name() ) );
textBridges.add( BridgeFactory.guessType( member ) );
}
+
+ DocumentId documentIdAnn = member.getAnnotation( DocumentId.class );
+ if ( documentIdAnn != null ) {
+ if ( idKeywordName != null ) {
+ throw new AssertionFailure( "Two document id assigned: "
+ + idKeywordName + " and " + BinderHelper.getAttributeName( member, documentIdAnn.name() ) );
+ }
+ idKeywordName = BinderHelper.getAttributeName( member, documentIdAnn.name() );
+ FieldBridge fieldBridge = BridgeFactory.guessType( member );
+ if ( fieldBridge instanceof IdFieldBridge ) {
+ idBridge = (IdFieldBridge) fieldBridge;
+ }
+ else {
+ throw new HibernateException( "Bridge for document id does not implement IdFieldBridge: " + member.getName() );
+ }
+ idBoost = getBoost( member );
+ }
+
+ org.hibernate.lucene.Field fieldAnn = member.getAnnotation( org.hibernate.lucene.Field.class );
+ if ( fieldAnn != null ) {
+ setAccessible( member );
+ fieldGetters.add( member );
+ fieldNames.add( BinderHelper.getAttributeName( member, fieldAnn.name() ) );
+ fieldStore.add( getStore( fieldAnn.store() ) );
+ fieldIndex.add( getIndex( fieldAnn.index() ) );
+ fieldBridges.add( BridgeFactory.guessType( member ) );
+ }
}
+ 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.Index getIndex(Index index) {
+ switch( index ) {
+ case NO:
+ return Field.Index.NO;
+ case NO_NORMS:
+ return Field.Index.NO_NORMS;
+ case TOKENIZED:
+ return Field.Index.TOKENIZED;
+ case UN_TOKENISED:
+ return Field.Index.UN_TOKENIZED;
+ default:
+ throw new AssertionFailure( "Unexpected Index: " + index );
+ }
+ }
+
private Float getBoost(XAnnotatedElement element) {
if ( element == null ) return null;
Boost boost = element.getAnnotation( Boost.class );
@@ -180,11 +247,19 @@
Field.Index.TOKENIZED, getBoost( member )
);
}
+ for ( int i = 0; i < fieldNames.size(); i++ ) {
+ XMember member = fieldGetters.get( i );
+ Object value = getMemberValue( instance, member );
+ fieldBridges.get( i ).set(
+ fieldNames.get( i ), value, doc, fieldStore.get( i ),
+ fieldIndex.get( i ), getBoost( member )
+ );
+ }
return doc;
}
public Term getTerm(Serializable id) {
- return new Term( idKeywordName, id.toString() );
+ return new Term( idKeywordName, idBridge.objectToString( id ) );
}
public DirectoryProvider getDirectoryProvider() {
@@ -201,7 +276,7 @@
}
}
- public FieldBridge getIdBridge() {
+ public IdFieldBridge getIdBridge() {
return idBridge;
}
Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentId.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentId.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentId.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -0,0 +1,22 @@
+//$Id: $
+package org.hibernate.lucene;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Declare a field as the document id. If set to a property, the property will be used
+ * TODO: If set to a class, the class itself will be passed to the FieldBridge
+ * Note that @{link org.hibernate.lucene.bridge.FieldBridge#get} must return the Entity id
+ *
+ * @author Emmanuel Bernard
+ */
+@Retention( RetentionPolicy.RUNTIME)
+(a)Target({ElementType.METHOD, ElementType.FIELD})
+@Documented
+public @interface DocumentId {
+ String name() default "";
+}
Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Field.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Field.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Field.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -0,0 +1,38 @@
+//$Id: $
+/**
+ * JavaDoc copy/pastle from the Apache Lucene project
+ * Available under the ASL 2.0 http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.hibernate.lucene;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Documented;
+
+/**
+ * Mark a property as indexable
+ *
+ * @author Emmanuel Bernard
+ */
+@Retention( RetentionPolicy.RUNTIME)
+(a)Target({ElementType.METHOD, ElementType.FIELD})
+@Documented
+public @interface Field {
+ /**
+ * Field name, default to the JavaBean property name
+ */
+ String name() default "";
+
+ /**
+ * Should the value be stored in the document
+ */
+ Store store() default Store.NO;
+
+ /**
+ * Defines how the Field should be indexed
+ */
+ Index index();
+
+}
Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Index.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Index.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Index.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -0,0 +1,29 @@
+//$Id: $
+package org.hibernate.lucene;
+
+/**
+ * Defines how an Field should be indexed
+ */
+public enum Index {
+ /** Do not index the field value. This field can thus not be searched,
+ * but one can still access its contents provided it is
+ * {@link Store stored}. */
+ NO,
+ /** Index the field's value so it can be searched. An Analyzer will be used
+ * to tokenize and possibly further normalize the text before its
+ * terms will be stored in the index. This is useful for common text.
+ */
+ TOKENIZED,
+ /** Index the field's value without using an Analyzer, so it can be searched.
+ * As no analyzer is used the value will be stored as a single term. This is
+ * useful for unique Ids like product numbers.
+ */
+ UN_TOKENISED,
+ /** Index the field's value without an Analyzer, and disable
+ * the storing of norms. No norms means that index-time boosting
+ * and field length normalization will be disabled. The benefit is
+ * less memory usage as norms take up one byte per indexed field
+ * for every document in the index.
+ */
+ NO_NORMS
+}
Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Keyword.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Keyword.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Keyword.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -13,7 +13,9 @@
/**
* Specifies that a property of an entity is a Lucene
* keyword field
+ * @deprecated use @Field(index=Index.UN_TOKENIZED, store=Store.YES) or @DocumentId when id=true was used
*/
+@Deprecated
public @interface Keyword {
/**
* The field name
Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Store.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Store.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Store.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -0,0 +1,13 @@
+//$Id: $
+package org.hibernate.lucene;
+
+/**
+ * Whether or not the value is stored in the document
+ *
+ * @author Emmanuel Bernard
+ */
+public enum Store {
+ NO,
+ YES,
+ COMPRESS
+}
Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Text.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Text.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Text.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -13,7 +13,9 @@
/**
* Specifies that a property of an entity is a Lucene
* text field
+ * @deprecated use @Field(index=Index.TOKENIZED, store=Store.YES)
*/
+@Deprecated
public @interface Text {
/**
* The field name
Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Unstored.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Unstored.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/Unstored.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -13,7 +13,9 @@
/**
* Specifies that a property of an entity is a Lucene
* unstored field
+ * @deprecated use @Field(index=Index.TOKENIZED, store=Store.NO)
*/
+@Deprecated
public @interface Unstored {
/**
* The field name
Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -13,6 +13,11 @@
//TODO should show Field or document?
//document is nice since I can save an object into several fields
public interface FieldBridge {
- Object get(String name, Document document);
- void set(String name, Object value, Document document, Field.Store store, Field.Index index, Float boost);
+ /**
+ * Manipulate the document to index the given value.
+ * A common implementation is to add a Field <code>name</code> to the given document following
+ * the parameters (<code>store</code>, <code>index</code>, <code>boost</code>) if the
+ * <code>value></code> is not null
+ */
+ void set(String name, Object value, Document document, Field.Store store, Field.Index index, Float boost);
}
Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IdFieldBridge.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IdFieldBridge.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IdFieldBridge.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -0,0 +1,29 @@
+//$Id: $
+package org.hibernate.lucene.bridge;
+
+import org.apache.lucene.document.Document;
+
+/**
+ * Any bridge expected to process a document id should implement this interface
+ *
+ * @author Emmanuel Bernard
+ */
+//FIXME rework the interface inheritance there are some common concepts with StringBridge
+public interface IdFieldBridge extends FieldBridge {
+ /**
+ * build the element from the Document
+ * This method is called when the bridge is used on a document id, If the Bridge is not expected to
+ * support document id, this method should raise an exception
+ * The return value is the Entity id
+ *
+ * @param name field name
+ * @param document document
+ */
+ Object get(String name, Document document);
+
+ /**
+ * convert the object representation to a String
+ * The return String must not be null, it can be empty though
+ */
+ String objectToString(Object object);
+}
Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -10,7 +10,8 @@
*
* @author Emmanuel Bernard
*/
-public class String2FieldBridgeAdaptor implements FieldBridge {
+//FIXME all String2FieldBridgeAdaptor are not IdFieldBridge
+public class String2FieldBridgeAdaptor implements FieldBridge, IdFieldBridge {
private StringBridge stringBridge;
@@ -22,7 +23,11 @@
return stringBridge.stringToObject( field.stringValue() );
}
- public void set(String name, Object value, Document document, Field.Store store, Field.Index index, Float boost) {
+ public String objectToString(Object object) {
+ return stringBridge.objectToString( object );
+ }
+
+ public void set(String name, Object value, Document document, Field.Store store, Field.Index index, Float boost) {
String indexedString = stringBridge.objectToString(value);
//Do not add fields on empty strings, seems a sensible default in most situations
if ( StringHelper.isNotEmpty( indexedString ) ) {
Modified: branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java
===================================================================
--- branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java 2006-10-18 16:14:49 UTC (rev 10610)
+++ branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java 2006-10-18 22:09:34 UTC (rev 10611)
@@ -11,6 +11,10 @@
import org.hibernate.lucene.Text;
import org.hibernate.lucene.Unstored;
import org.hibernate.lucene.Boost;
+import org.hibernate.lucene.Field;
+import org.hibernate.lucene.Index;
+import org.hibernate.lucene.DocumentId;
+import org.hibernate.lucene.Store;
@Entity
@Indexed(index = "Documents")
@@ -32,8 +36,9 @@
@Id
@GeneratedValue
- @Keyword(id = true)
- public Long getId() {
+ //@Keyword(id = true)
+ @DocumentId
+ public Long getId() {
return id;
}
@@ -41,8 +46,9 @@
this.id = id;
}
- @Text
- @Boost(2)
+ //@Text
+ @Field( store = Store.YES, index = Index.TOKENIZED )
+ @Boost(2)
public String getTitle() {
return title;
}
@@ -51,8 +57,9 @@
this.title = title;
}
- @Unstored(name = "Abstract")
- public String getSummary() {
+ //@Unstored(name = "Abstract")
+ @Field( name="Abstract", store = Store.NO, index = Index.TOKENIZED )
+ public String getSummary() {
return summary;
}
@@ -61,8 +68,9 @@
}
@Lob
- @Unstored
- public String getText() {
+ //@Unstored
+ @Field( store = Store.NO, index = Index.TOKENIZED )
+ public String getText() {
return text;
}
17 years, 6 months