Hibernate SVN: r11263 - branches/Branch_3_2/HibernateExt/search/doc/reference/en/modules.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-03-07 20:42:10 -0500 (Wed, 07 Mar 2007)
New Revision: 11263
Modified:
branches/Branch_3_2/HibernateExt/search/doc/reference/en/modules/mapping.xml
Log:
HSEARCH-27 documentation on @indexedEmbedded
Modified: branches/Branch_3_2/HibernateExt/search/doc/reference/en/modules/mapping.xml
===================================================================
--- branches/Branch_3_2/HibernateExt/search/doc/reference/en/modules/mapping.xml 2007-03-07 23:56:43 UTC (rev 11262)
+++ branches/Branch_3_2/HibernateExt/search/doc/reference/en/modules/mapping.xml 2007-03-08 01:42:10 UTC (rev 11263)
@@ -10,81 +10,84 @@
<section id="search-mapping-entity" revision="3">
<title>Mapping an entity</title>
- <para>First, we must declare a persistent class as indexable. This is done
- by annotating the class with <literal>@Indexed</literal> (all entities not
- annotated with <literal>@Indexed</literal> will be ignored by the indexing
- process):</para>
+ <section>
+ <title>Basic mapping</title>
- <programlisting>@Entity
+ <para>First, we must declare a persistent class as indexable. This is
+ done by annotating the class with <literal>@Indexed</literal> (all
+ entities not annotated with <literal>@Indexed</literal> will be ignored
+ by the indexing process):</para>
+
+ <programlisting>@Entity
<emphasis role="bold">@Indexed(index="indexes/essays")</emphasis>
public class Essay {
...
}</programlisting>
- <para>The <literal>index</literal> attribute tells Hibernate what the
- Lucene directory name is (usually a directory on your file system). If you
- wish to define a base directory for all Lucene indexes, you can use the
- <literal>hibernate.search.default.indexBase</literal> property in your
- configuration file. Each entity instance will be represented by a Lucene
- <classname>Document</classname> inside the given index (aka
- Directory).</para>
+ <para>The <literal>index</literal> attribute tells Hibernate what the
+ Lucene directory name is (usually a directory on your file system). If
+ you wish to define a base directory for all Lucene indexes, you can use
+ the <literal>hibernate.search.default.indexBase</literal> property in
+ your configuration file. Each entity instance will be represented by a
+ Lucene <classname>Document</classname> inside the given index (aka
+ Directory).</para>
- <para>For each property (or attribute) of your entity, you have the
- ability to describe how it will be indexed. The default (ie no annotation)
- means that the property is completly ignored by the indexing process.
- <literal>@Field</literal> does declare a property as indexed. When
- indexing an element to a Lucene document you can specify how it is
- indexed:</para>
+ <para>For each property (or attribute) of your entity, you have the
+ ability to describe how it will be indexed. The default (ie no
+ annotation) means that the property is completly ignored by the indexing
+ process. <literal>@Field</literal> does declare a property as indexed.
+ When indexing an element to a Lucene document you can specify how it is
+ indexed:</para>
- <itemizedlist>
- <listitem>
- <para><literal>name</literal> : describe under which name, the
- property should be stored in the Lucene Document. The default value is
- the property name (following the JavaBeans convention)</para>
- </listitem>
+ <itemizedlist>
+ <listitem>
+ <para><literal>name</literal> : describe under which name, the
+ property should be stored in the Lucene Document. The default value
+ is the property name (following the JavaBeans convention)</para>
+ </listitem>
- <listitem>
- <para><literal>store</literal> : describe whether or not the property
- is stored in the Lucene index. You can store the value
- <literal>Store.YES</literal> (comsuming more space in the index),
- store it in a compressed way <literal>Store.COMPRESS</literal> (this
- does consume more CPU), or avoid any storage
- <literal>Store.NO</literal> (this is the default value). When a
- property is stored, you can retrieve it from the Lucene Document (note
- that this is not related to whether the element is indexed or
- not).</para>
- </listitem>
+ <listitem>
+ <para><literal>store</literal> : describe whether or not the
+ property is stored in the Lucene index. You can store the value
+ <literal>Store.YES</literal> (comsuming more space in the index),
+ store it in a compressed way <literal>Store.COMPRESS</literal> (this
+ does consume more CPU), or avoid any storage
+ <literal>Store.NO</literal> (this is the default value). When a
+ property is stored, you can retrieve it from the Lucene Document
+ (note that this is not related to whether the element is indexed or
+ not).</para>
+ </listitem>
- <listitem>
- <para>index: describe how the element is indexed (ie the process used
- to index the property and the type of information store). The
- different values are <literal>Index.NO</literal> (no indexing, ie
- cannot be found by a query), <literal>Index.TOKENIZED</literal> (use
- an analyzer to process the property),
- <literal>Index.UN_TOKENISED</literal> (no analyzer pre processing),
- <literal>Index.NO_NORM</literal> (do not store the normalization
- data).</para>
- </listitem>
- </itemizedlist>
+ <listitem>
+ <para>index: describe how the element is indexed (ie the process
+ used to index the property and the type of information store). The
+ different values are <literal>Index.NO</literal> (no indexing, ie
+ cannot be found by a query), <literal>Index.TOKENIZED</literal> (use
+ an analyzer to process the property),
+ <literal>Index.UN_TOKENISED</literal> (no analyzer pre processing),
+ <literal>Index.NO_NORM</literal> (do not store the normalization
+ data).</para>
+ </listitem>
+ </itemizedlist>
- <para>These attributes are part of the <literal>@Field</literal>
- annotation.</para>
+ <para>These attributes are part of the <literal>@Field</literal>
+ annotation.</para>
- <para>Whether or not you want to store the data depends on how you wish to
- use the index query result. As of today, for a pure <productname>Hibernate
- Search </productname> usage, storing is not necessary. Whether or not you
- want to tokenize a property or not depends on whether you wish to search
- the element as is, or only normalized part of it. It make sense to
- tokenize a text field, but it does not to do it for a date field (or an id
- field).</para>
+ <para>Whether or not you want to store the data depends on how you wish
+ to use the index query result. As of today, for a pure
+ <productname>Hibernate Search </productname> usage, storing is not
+ necessary. Whether or not you want to tokenize a property or not depends
+ on whether you wish to search the element as is, or only normalized part
+ of it. It make sense to tokenize a text field, but it does not to do it
+ for a date field (or an id field).</para>
- <para>Finally, the id property of an entity is a special property used by
- <productname>Hibernate Search</productname> to ensure index unicity of a
- given entity. By design, an id has to be stored and must not be tokenized.
- To mark a property as index id, use the <literal>@DocumentId</literal>
- annotation.</para>
+ <para>Finally, the id property of an entity is a special property used
+ by <productname>Hibernate Search</productname> to ensure index unicity
+ of a given entity. By design, an id has to be stored and must not be
+ tokenized. To mark a property as index id, use the
+ <literal>@DocumentId</literal> annotation.</para>
- <programlisting>@Entity
+ <programlisting>@Entity
@Indexed(index="indexes/essays")
public class Essay {
...
@@ -101,21 +104,150 @@
public String getText() { return text; }
}</programlisting>
- <para>These annotations define an index with three fields:
- <literal>id</literal> , <literal>Abstract</literal> and
- <literal>text</literal> . Note that by default the field name is
- decapitalized, following the JavaBean specification.</para>
+ <para>These annotations define an index with three fields:
+ <literal>id</literal> , <literal>Abstract</literal> and
+ <literal>text</literal> . Note that by default the field name is
+ decapitalized, following the JavaBean specification.</para>
- <para>Note: you <emphasis>must</emphasis> specify
- <literal>@DocumentId</literal> on the identifier property of your entity
- class.</para>
+ <note>
+ <para>You <emphasis>must</emphasis> specify
+ <literal>@DocumentId</literal> on the identifier property of your
+ entity class.</para>
+ </note>
+ </section>
- <para>Lucene has the notion of <emphasis>boost factor</emphasis> . It's a
- way to give more weigth to a field or to an indexed element over an other
- during the indexation process. You can use <literal>@Boost</literal> at
- the field or the class level.</para>
+ <section>
+ <title>Embedded and associated objects</title>
- <programlisting>@Entity
+ <para>Associated objects as well as embedded objects can be indexed as
+ well as part of the root entity index.</para>
+
+ <programlisting>@Entity
+@Indexed
+public class Place {
+ @Id
+ @GeneratedValue
+ @DocumentId
+ private Long id;
+
+ @Field( index = Index.TOKENIZED )
+ private String name;
+
+ @OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } )
+ <emphasis role="bold">@IndexedEmbedded</emphasis>
+ private Address address;
+ ....
+}
+
+@Entity
+@Indexed
+public class Address {
+ @Id
+ @GeneratedValue
+ @DocumentId
+ private Long id;
+
+ @Field(index=Index.TOKENIZED)
+ private String street;
+
+ @Field(index=Index.TOKENIZED)
+ private String city;
+
+ <emphasis role="bold">@IndexedEmbedded(depth = 1, prefix = "ownedBy_")</emphasis>
+ private Owner ownedBy;
+ ...
+}
+
+@Embeddable
+public class Owner {
+ @Field(index = Index.TOKENIZED)
+ private String name;
+ ...
+}</programlisting>
+
+ <para>Any <literal>@*ToOne</literal> and <literal>@Embedded</literal>
+ attribute can be annotated with <literal>@IndexedEmbedded</literal>. The
+ attributes of the associated class will then be added to the main entity
+ index. In the previous example, the index will contain the following
+ fields</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>id</para>
+ </listitem>
+
+ <listitem>
+ <para>name</para>
+ </listitem>
+
+ <listitem>
+ <para>address.street</para>
+ </listitem>
+
+ <listitem>
+ <para>address.city</para>
+ </listitem>
+
+ <listitem>
+ <para>addess.ownedBy_name</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>The default prefix is <literal>propertyName.</literal>, following
+ the traditional object navigation convention. You can override it using
+ the <literal>prefix</literal> attribute.</para>
+
+ <para><literal>depth</literal> is necessary when the object graph
+ contains a cyclic dependency of classes (not instance). For example, if
+ <classname>Owner</classname> points to <classname>Place</classname>.
+ Hibernate Search will stop including Indexed embedded atttributes after
+ reaching the expected depth (or is the object graph boundaries are
+ reached). A class having a self reference is an example of cyclic
+ dependency. In our example, because <literal>depth</literal> is set to
+ 1, any <literal>@IndexedEmbedded</literal> attribute in Owner (if any)
+ will be ignored.</para>
+
+ <para>Such a mapping is very useful to express queries refering to
+ associated objects, such as:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>Return places where name contains JBoss and where address city
+ is Atlanta. In Lucene query this would be</para>
+
+ <programlisting>+name:jboss +address.city:atlanta </programlisting>
+ </listitem>
+
+ <listitem>
+ <para>Return places where name contains JBoss and where owner's name
+ contain Joe. In Lucene query this would be</para>
+
+ <programlisting>+name:jboss +address.orderBy_name:joe </programlisting>
+ </listitem>
+ </itemizedlist>
+
+ <para>In a way it mimics the relational join operation in a more
+ efficient way (at the cost of data duplication). Remember that, out of
+ the box, Lucene indexes have no notion of association, the join
+ operation is simply non-existent. It might help to keep the relational
+ model normalzed while benefiting from the full text index speed and
+ feature richness.</para>
+
+ <para><note>
+ <para>An associated object can itself be (but don't have to)
+ <literal>@Indexed</literal> </para>
+ </note></para>
+ </section>
+
+ <section>
+ <title>Boost factor</title>
+
+ <para>Lucene has the notion of <emphasis>boost factor</emphasis> . It's
+ a way to give more weigth to a field or to an indexed element over an
+ other during the indexation process. You can use
+ <literal>@Boost</literal> at the field or the class level.</para>
+
+ <programlisting>@Entity
@Indexed(index="indexes/essays")
<emphasis role="bold">@Boost(2)</emphasis>
public class Essay {
@@ -134,18 +266,23 @@
public String getText() { return text; }
} </programlisting>
- <para>In our example, Essay's probability to reach the top of the search
- list will be multiplied by 2 and the summary field will be 2.5 more
- important than the test field. Note that this explaination is actually
- wrong, but it is simple and close enought to the reality. Please check the
- Lucene documentation or the excellent <citetitle>Lucene In Action
- </citetitle> from Otis Gospodnetic and Erik Hatcher.</para>
+ <para>In our example, Essay's probability to reach the top of the search
+ list will be multiplied by 2 and the summary field will be 2.5 more
+ important than the test field. Note that this explaination is actually
+ wrong, but it is simple and close enought to the reality. Please check
+ the Lucene documentation or the excellent <citetitle>Lucene In Action
+ </citetitle> from Otis Gospodnetic and Erik Hatcher.</para>
+ </section>
- <para>The analyzer class used to index the elements is configurable
- through the <literal>hibernate.search.analyzer</literal> property. If none
- defined,
- <classname>org.apache.lucene.analysis.standard.StandardAnalyzer</classname>
- is used as the default.</para>
+ <section>
+ <title>Analyser</title>
+
+ <para>The analyzer class used to index the elements is configurable
+ through the <literal>hibernate.search.analyzer</literal> property. If
+ none defined,
+ <classname>org.apache.lucene.analysis.standard.StandardAnalyzer</classname>
+ is used as the default.</para>
+ </section>
</section>
<section id="search-mapping-bridge">
17 years, 1 month
Hibernate SVN: r11262 - branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-03-07 18:56:43 -0500 (Wed, 07 Mar 2007)
New Revision: 11262
Added:
branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/Music.java
Modified:
branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/RemoveTest.java
Log:
mote tests about removing an updated entity
Added: branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/Music.java
===================================================================
--- branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/Music.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/Music.java 2007-03-07 23:56:43 UTC (rev 11262)
@@ -0,0 +1,43 @@
+//$Id: $
+package org.hibernate.ejb.test.emops;
+
+import javax.persistence.Version;
+import javax.persistence.Id;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class Music {
+ @Id @GeneratedValue
+ private Integer id;
+ private String name;
+ @Version
+ private Integer version;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getVersion() {
+ return version;
+ }
+
+ public void setVersion(Integer version) {
+ this.version = version;
+ }
+}
Modified: branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/RemoveTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/RemoveTest.java 2007-03-07 23:28:34 UTC (rev 11261)
+++ branches/Branch_3_2/HibernateExt/entitymanager/src/test/org/hibernate/ejb/test/emops/RemoveTest.java 2007-03-07 23:56:43 UTC (rev 11262)
@@ -1,7 +1,9 @@
//$Id: $
package org.hibernate.ejb.test.emops;
+import java.util.Map;
import javax.persistence.EntityManager;
+import javax.persistence.OptimisticLockException;
import org.hibernate.ejb.test.TestCase;
@@ -35,10 +37,59 @@
em.close();
}
+ public void testUpdatedAndRemove() {
+ Music music = new Music();
+ music.setName( "Classical" );
+ EntityManager em = factory.createEntityManager();
+ em.getTransaction().begin();
+ em.persist( music );
+ em.getTransaction().commit();
+
+ em.clear();
+
+ em = factory.createEntityManager();
+ em.getTransaction().begin();
+ EntityManager em2 = factory.createEntityManager();
+ em2.getTransaction().begin();
+
+ //read music from 2nd EM
+ music = em2.find( Music.class, music.getId() );
+
+ //change music
+ em.find( Music.class, music.getId() ).setName( "Rap" );
+ em.getTransaction().commit();
+
+ try {
+ em2.remove( music ); //remove changed music
+ em2.flush();
+ fail("should have an optimistic lock exception");
+ }
+ catch( OptimisticLockException e ) {
+ //success
+ }
+ finally {
+ em2.getTransaction().rollback();
+ }
+
+ //clean
+ em.remove( em.find( Music.class, music.getId() ) );
+
+ em.close();
+ em2.close();
+ }
+
public Class[] getAnnotatedClasses() {
return new Class[] {
Race.class,
- Competitor.class
+ Competitor.class,
+ Music.class
};
}
+
+
+ public Map getConfig() {
+ Map cfg = super.getConfig();
+ cfg.put( "hibernate.jdbc.batch_size", "0");
+ return cfg;
+ }
}
17 years, 1 month
Hibernate SVN: r11261 - branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-03-07 18:28:34 -0500 (Wed, 07 Mar 2007)
New Revision: 11261
Modified:
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java
Log:
test cleans up after its work
Modified: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java 2007-03-07 23:02:51 UTC (rev 11260)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java 2007-03-07 23:28:34 UTC (rev 11261)
@@ -50,6 +50,10 @@
result = session.createFullTextQuery(query, Tower.class).list();
assertEquals( "unable to find property in embedded", 1, result.size() );
+ tx = s.beginTransaction();
+ s.delete( s.get(Tower.class, tower.getId() ) );
+ tx.commit();
+
s.close();
}
Modified: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java 2007-03-07 23:02:51 UTC (rev 11260)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java 2007-03-07 23:28:34 UTC (rev 11261)
@@ -27,7 +27,7 @@
@Field( index = Index.TOKENIZED )
private String name;
- @OneToOne( cascade = CascadeType.PERSIST )
+ @OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } )
@IndexedEmbedded
private Address address;
17 years, 1 month
Hibernate SVN: r11260 - in branches/Branch_3_2/HibernateExt/search/src: java/org/hibernate/search/engine and 2 other directories.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-03-07 18:02:51 -0500 (Wed, 07 Mar 2007)
New Revision: 11260
Added:
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/annotations/IndexedEmbedded.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Address.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Owner.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java
Modified:
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java
Log:
HSEARCH-27
Added: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/annotations/IndexedEmbedded.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/annotations/IndexedEmbedded.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/annotations/IndexedEmbedded.java 2007-03-07 23:02:51 UTC (rev 11260)
@@ -0,0 +1,31 @@
+//$Id: $
+package org.hibernate.search.annotations;
+
+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;
+
+@Retention( RetentionPolicy.RUNTIME )
+@Target( { ElementType.FIELD, ElementType.METHOD } )
+@Documented
+/**
+ * Specifies that an association (@*ToOne or @Embedded) is to be indexed
+ * in the root entity index
+ * It allows queries involving associated objects restrictions
+ */
+public @interface IndexedEmbedded {
+ /**
+ * Field name prefix
+ * Default to 'propertyname.'
+ */
+ String prefix() default ".";
+
+ /**
+ * Stop indexing embedded elements when depth is reached
+ * depth=1 means the associated element is index, but not its embedded elements
+ * Default: infinite (an exception will be raised in case of class circular reference when infinite is chosen)
+ */
+ int depth() default Integer.MAX_VALUE;
+}
Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java 2007-03-07 22:55:12 UTC (rev 11259)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/engine/DocumentBuilder.java 2007-03-07 23:02:51 UTC (rev 11260)
@@ -8,11 +8,16 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.HashMap;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.Term;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
@@ -28,12 +33,14 @@
import org.hibernate.search.annotations.Store;
import org.hibernate.search.annotations.Text;
import org.hibernate.search.annotations.Unstored;
+import org.hibernate.search.annotations.IndexedEmbedded;
import org.hibernate.search.bridge.BridgeFactory;
import org.hibernate.search.bridge.FieldBridge;
import org.hibernate.search.bridge.TwoWayFieldBridge;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.util.BinderHelper;
import org.hibernate.util.ReflectHelper;
+import org.hibernate.Hibernate;
/**
* Set up and provide a manager for indexes classes
@@ -44,21 +51,9 @@
* @author Richard Hallier
*/
public class DocumentBuilder<T> {
- private final List<XMember> keywordGetters = new ArrayList<XMember>();
- private final List<String> keywordNames = new ArrayList<String>();
- private final List<FieldBridge> keywordBridges = new ArrayList<FieldBridge>();
- private final List<XMember> unstoredGetters = new ArrayList<XMember>();
- private final List<String> unstoredNames = new ArrayList<String>();
- private final List<FieldBridge> unstoredBridges = new ArrayList<FieldBridge>();
- 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 static final Log log = LogFactory.getLog( DocumentBuilder.class );
+ private final PropertiesMetadata rootPropertiesMetadata = new PropertiesMetadata();
private final XClass beanClass;
private final DirectoryProvider directoryProvider;
private String idKeywordName;
@@ -68,6 +63,8 @@
private TwoWayFieldBridge idBridge;
private Set<Class> mappedSubclasses = new HashSet<Class>();
private ReflectionManager reflectionManager;
+ private int level = 0;
+ private int maxLevel = Integer.MAX_VALUE;
public DocumentBuilder(XClass clazz, Analyzer analyzer, DirectoryProvider directory,
@@ -78,31 +75,41 @@
//FIXME get rid of it when boost is stored?
this.reflectionManager = reflectionManager;
- if ( clazz == null ) throw new AssertionFailure( "Unable to build a DocumentBuilder with a null class" );
+ if ( clazz == null ) throw new AssertionFailure( "Unable to build a DocumemntBuilder with a null class" );
- for ( XClass currClass = beanClass; currClass != null; currClass = currClass.getSuperclass() ) {
+ rootPropertiesMetadata.boost = getBoost( clazz );
+ Set<XClass> processedClasses = new HashSet<XClass>();
+ processedClasses.add( clazz );
+ initializeMembers(clazz, rootPropertiesMetadata, true, "", processedClasses );
+ //processedClasses.remove( clazz ); for the sake of completness
+
+ if ( idKeywordName == null ) {
+ throw new SearchException( "No document id for: " + clazz.getName() );
+ }
+ }
+
+ private void initializeMembers(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
+ Set<XClass> processedClasses) {
+ for ( XClass currClass = clazz; currClass != null; currClass = currClass.getSuperclass() ) {
//rejecting non properties because the object is loaded from Hibernate, so indexing a non property does not make sense
List<XProperty> methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
for ( XProperty method : methods ) {
- initializeMember( method );
+ initializeMember( method, propertiesMetadata, isRoot, prefix, processedClasses );
}
List<XProperty> fields = currClass.getDeclaredProperties( XClass.ACCESS_FIELD );
for ( XProperty field : fields ) {
- initializeMember( field );
+ initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses );
}
}
-
- if ( idKeywordName == null ) {
- throw new SearchException( "No document id for: " + clazz.getName() );
- }
}
- private void initializeMember(XProperty member) {
+ private void initializeMember(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
+ String prefix, Set<XClass> processedClasses) {
Keyword keywordAnn = member.getAnnotation( Keyword.class );
if ( keywordAnn != null ) {
- String name = BinderHelper.getAttributeName( member, keywordAnn.name() );
- if ( keywordAnn.id() ) {
+ String name = prefix + BinderHelper.getAttributeName( member, keywordAnn.name() );
+ if ( isRoot && keywordAnn.id() ) {
idKeywordName = name;
idBoost = getBoost( member );
FieldBridge fieldBridge = BridgeFactory.guessType( member );
@@ -116,35 +123,35 @@
}
else {
setAccessible( member );
- keywordGetters.add( member );
- keywordNames.add( name );
- keywordBridges.add( BridgeFactory.guessType( member ) );
+ propertiesMetadata.keywordGetters.add( member );
+ propertiesMetadata.keywordNames.add( name );
+ propertiesMetadata.keywordBridges.add( BridgeFactory.guessType( member ) );
}
}
Unstored unstoredAnn = member.getAnnotation( Unstored.class );
if ( unstoredAnn != null ) {
setAccessible( member );
- unstoredGetters.add( member );
- unstoredNames.add( BinderHelper.getAttributeName( member, unstoredAnn.name() ) );
- unstoredBridges.add( BridgeFactory.guessType( member ) );
+ propertiesMetadata.unstoredGetters.add( member );
+ propertiesMetadata.unstoredNames.add( prefix + BinderHelper.getAttributeName( member, unstoredAnn.name() ) );
+ propertiesMetadata.unstoredBridges.add( BridgeFactory.guessType( member ) );
}
Text textAnn = member.getAnnotation( Text.class );
if ( textAnn != null ) {
setAccessible( member );
- textGetters.add( member );
- textNames.add( BinderHelper.getAttributeName( member, textAnn.name() ) );
- textBridges.add( BridgeFactory.guessType( member ) );
+ propertiesMetadata.textGetters.add( member );
+ propertiesMetadata.textNames.add( prefix + BinderHelper.getAttributeName( member, textAnn.name() ) );
+ propertiesMetadata.textBridges.add( BridgeFactory.guessType( member ) );
}
DocumentId documentIdAnn = member.getAnnotation( DocumentId.class );
- if ( documentIdAnn != null ) {
+ if ( isRoot && 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() );
+ idKeywordName = prefix + BinderHelper.getAttributeName( member, documentIdAnn.name() );
FieldBridge fieldBridge = BridgeFactory.guessType( member );
if ( fieldBridge instanceof TwoWayFieldBridge ) {
idBridge = (TwoWayFieldBridge) fieldBridge;
@@ -160,14 +167,63 @@
member.getAnnotation( org.hibernate.search.annotations.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 ) );
+ propertiesMetadata.fieldGetters.add( member );
+ propertiesMetadata.fieldNames.add( prefix + BinderHelper.getAttributeName( member, fieldAnn.name() ) );
+ propertiesMetadata.fieldStore.add( getStore( fieldAnn.store() ) );
+ propertiesMetadata.fieldIndex.add( getIndex( fieldAnn.index() ) );
+ propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( member ) );
}
+
+ IndexedEmbedded embeddedAnn = member.getAnnotation( IndexedEmbedded.class );
+ if ( embeddedAnn != null ) {
+ int oldMaxLevel = maxLevel;
+ maxLevel = embeddedAnn.depth() + level > maxLevel ? maxLevel : embeddedAnn.depth() + level;
+ level++;
+
+ if ( maxLevel == Integer.MAX_VALUE //infinite
+ && processedClasses.contains( member.getClassOrElementClass() ) ) {
+ throw new SearchException(
+ "Circular reference. Duplicate use of "
+ + member.getClassOrElementClass().getName()
+ + " in root entity " + beanClass.getName()
+ + "#" + buildEmbeddedPrefix( prefix, embeddedAnn, member )
+ );
+ }
+ if (level <= maxLevel) {
+ processedClasses.add( member.getClassOrElementClass() ); //push
+
+ setAccessible( member );
+ propertiesMetadata.embeddedGetters.add( member );
+ PropertiesMetadata metadata = new PropertiesMetadata();
+ propertiesMetadata.embeddedPropertiesMetadata.add(metadata);
+ metadata.boost = getBoost( member );
+ String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
+ initializeMembers( member.getClassOrElementClass(), metadata, false, localPrefix, processedClasses);
+
+ processedClasses.remove( member.getClassOrElementClass() ); //pop
+ }
+ else if ( log.isTraceEnabled() ) {
+ String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
+ log.trace( "depth reached, ignoring " + localPrefix );
+ }
+
+ level--;
+ maxLevel = oldMaxLevel; //set back the the old max level
+ }
}
+ private String buildEmbeddedPrefix(String prefix, IndexedEmbedded embeddedAnn, XProperty member) {
+ String localPrefix = prefix;
+ if ( ".".equals( embeddedAnn.prefix() ) ) {
+ //default to property name
+ localPrefix += member.getName() + '.';
+ }
+ else {
+ localPrefix += embeddedAnn.prefix();
+ }
+ return localPrefix;
+ }
+
private Field.Store getStore(Store store) {
switch (store) {
case NO:
@@ -204,7 +260,7 @@
null;
}
- private Object getMemberValue(T bean, XMember getter) {
+ private Object getMemberValue(Object bean, XMember getter) {
Object value;
try {
value = getter.invoke( bean );
@@ -218,9 +274,8 @@
public Document getDocument(T instance, Serializable id) {
Document doc = new Document();
XClass instanceClass = reflectionManager.toXClass( instance.getClass() );
- Float boost = getBoost( instanceClass );
- if ( boost != null ) {
- doc.setBoost( boost );
+ if ( rootPropertiesMetadata.boost != null ) {
+ doc.setBoost( rootPropertiesMetadata.boost );
}
{
Field classField =
@@ -228,39 +283,52 @@
doc.add( classField );
idBridge.set( idKeywordName, id, doc, Field.Store.YES, Field.Index.UN_TOKENIZED, idBoost );
}
- for ( int i = 0; i < keywordNames.size(); i++ ) {
- XMember member = keywordGetters.get( i );
+ buildDocumentFields( instance, doc, rootPropertiesMetadata );
+ return doc;
+ }
+
+ private void buildDocumentFields(Object instance, Document doc, PropertiesMetadata propertiesMetadata) {
+ if (instance == null) return;
+
+ for ( int i = 0; i < propertiesMetadata.keywordNames.size(); i++ ) {
+ XMember member = propertiesMetadata.keywordGetters.get( i );
Object value = getMemberValue( instance, member );
- keywordBridges.get( i ).set(
- keywordNames.get( i ), value, doc, Field.Store.YES,
+ propertiesMetadata.keywordBridges.get( i ).set(
+ propertiesMetadata.keywordNames.get( i ), value, doc, Field.Store.YES,
Field.Index.UN_TOKENIZED, getBoost( member )
);
}
- for ( int i = 0; i < textNames.size(); i++ ) {
- XMember member = textGetters.get( i );
+ for ( int i = 0; i < propertiesMetadata.textNames.size(); i++ ) {
+ XMember member = propertiesMetadata.textGetters.get( i );
Object value = getMemberValue( instance, member );
- textBridges.get( i ).set(
- textNames.get( i ), value, doc, Field.Store.YES,
+ propertiesMetadata.textBridges.get( i ).set(
+ propertiesMetadata.textNames.get( i ), value, doc, Field.Store.YES,
Field.Index.TOKENIZED, getBoost( member )
);
}
- for ( int i = 0; i < unstoredNames.size(); i++ ) {
- XMember member = unstoredGetters.get( i );
+ for ( int i = 0; i < propertiesMetadata.unstoredNames.size(); i++ ) {
+ XMember member = propertiesMetadata.unstoredGetters.get( i );
Object value = getMemberValue( instance, member );
- unstoredBridges.get( i ).set(
- unstoredNames.get( i ), value, doc, Field.Store.NO,
+ propertiesMetadata.unstoredBridges.get( i ).set(
+ propertiesMetadata.unstoredNames.get( i ), value, doc, Field.Store.NO,
Field.Index.TOKENIZED, getBoost( member )
);
}
- for ( int i = 0; i < fieldNames.size(); i++ ) {
- XMember member = fieldGetters.get( i );
+ for ( int i = 0; i < propertiesMetadata.fieldNames.size(); i++ ) {
+ XMember member = propertiesMetadata.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 )
+ propertiesMetadata.fieldBridges.get( i ).set(
+ propertiesMetadata.fieldNames.get( i ), value, doc, propertiesMetadata.fieldStore.get( i ),
+ propertiesMetadata.fieldIndex.get( i ), getBoost( member )
);
}
- return doc;
+ for ( int i = 0; i < propertiesMetadata.embeddedGetters.size(); i++ ) {
+ XMember member = propertiesMetadata.embeddedGetters.get( i );
+ Object value = getMemberValue( instance, member );
+ //if ( ! Hibernate.isInitialized( value ) ) continue; //this sounds like a bad idea
+ //TODO handle boost at embedded level: already stored in propertiesMedatada.boost
+ buildDocumentFields( value, doc, propertiesMetadata.embeddedPropertiesMetadata.get( i ) );
+ }
}
public Term getTerm(Serializable id) {
@@ -320,4 +388,24 @@
public Set<Class> getMappedSubclasses() {
return mappedSubclasses;
}
+
+ private static class PropertiesMetadata {
+ public Float boost = null;
+ public final List<XMember> keywordGetters = new ArrayList<XMember>();
+ public final List<String> keywordNames = new ArrayList<String>();
+ public final List<FieldBridge> keywordBridges = new ArrayList<FieldBridge>();
+ public final List<XMember> unstoredGetters = new ArrayList<XMember>();
+ public final List<String> unstoredNames = new ArrayList<String>();
+ public final List<FieldBridge> unstoredBridges = new ArrayList<FieldBridge>();
+ public final List<XMember> textGetters = new ArrayList<XMember>();
+ public final List<String> textNames = new ArrayList<String>();
+ public final List<FieldBridge> textBridges = new ArrayList<FieldBridge>();
+ public final List<String> fieldNames = new ArrayList<String>();
+ public final List<XMember> fieldGetters = new ArrayList<XMember>();
+ public final List<FieldBridge> fieldBridges = new ArrayList<FieldBridge>();
+ public final List<Field.Store> fieldStore = new ArrayList<Field.Store>();
+ public final List<Field.Index> fieldIndex = new ArrayList<Field.Index>();
+ public final List<XMember> embeddedGetters = new ArrayList<XMember>();
+ public final List<PropertiesMetadata> embeddedPropertiesMetadata = new ArrayList<PropertiesMetadata>();
+ }
}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Address.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Address.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Address.java 2007-03-07 23:02:51 UTC (rev 11260)
@@ -0,0 +1,69 @@
+//$Id: $
+package org.hibernate.search.test.embedded;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.OneToOne;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.IndexedEmbedded;
+
+/**
+ * @author Emmanuel Bernard
+ */
+
+@Entity
+@Indexed
+public class Address {
+ @Id
+ @GeneratedValue
+ @DocumentId
+ private Long id;
+
+ @Field(index= Index.TOKENIZED)
+ private String street;
+
+ @IndexedEmbedded(depth = 1, prefix = "ownedBy_")
+ private Owner ownedBy;
+
+ @OneToOne(mappedBy = "address")
+ private Tower tower;
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getStreet() {
+ return street;
+ }
+
+ public void setStreet(String street) {
+ this.street = street;
+ }
+
+ public Owner getOwnedBy() {
+ return ownedBy;
+ }
+
+ public void setOwnedBy(Owner ownedBy) {
+ this.ownedBy = ownedBy;
+ }
+
+
+ public Tower getTower() {
+ return tower;
+ }
+
+ public void setTower(Tower tower) {
+ this.tower = tower;
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/EmbeddedTest.java 2007-03-07 23:02:51 UTC (rev 11260)
@@ -0,0 +1,63 @@
+//$Id: $
+package org.hibernate.search.test.embedded;
+
+import java.util.List;
+
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.search.Query;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class EmbeddedTest extends SearchTestCase {
+
+ public void testEmbeddedIndexing() throws Exception {
+ Tower tower = new Tower();
+ tower.setName( "JBoss tower" );
+ Address a = new Address();
+ a.setStreet( "Tower place");
+ a.setTower( tower );
+ tower.setAddress( a );
+ Owner o = new Owner();
+ o.setName( "Atlanta Renting corp" );
+ a.setOwnedBy( o );
+ o.setAddress( a );
+
+ Session s = openSession( );
+ Transaction tx = s.beginTransaction();
+ s.persist( tower );
+ tx.commit();
+
+ s.clear();
+
+
+ FullTextSession session = Search.createFullTextSession(s);
+ QueryParser parser = new QueryParser("id", new StandardAnalyzer() );
+ Query query;
+ List result;
+
+ query = parser.parse("address.street:place");
+ result = session.createFullTextQuery(query).list();
+ assertEquals( "unable to find property in embedded", 1, result.size() );
+
+ query = parser.parse("address.ownedBy_name:renting");
+ result = session.createFullTextQuery(query, Tower.class).list();
+ assertEquals( "unable to find property in embedded", 1, result.size() );
+
+ s.close();
+
+ }
+
+ protected Class[] getMappings() {
+ return new Class[] {
+ Tower.class,
+ Address.class
+ };
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Owner.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Owner.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Owner.java 2007-03-07 23:02:51 UTC (rev 11260)
@@ -0,0 +1,39 @@
+//$Id: $
+package org.hibernate.search.test.embedded;
+
+import javax.persistence.Embeddable;
+
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.IndexedEmbedded;
+import org.hibernate.annotations.Parent;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Embeddable
+public class Owner {
+ @Field(index = Index.TOKENIZED)
+ private String name;
+
+ @Parent
+ @IndexedEmbedded //play the lunatic user
+ private Address address;
+
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/embedded/Tower.java 2007-03-07 23:02:51 UTC (rev 11260)
@@ -0,0 +1,58 @@
+//$Id: $
+package org.hibernate.search.test.embedded;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.IndexedEmbedded;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+@Indexed
+public class Tower {
+ @Id
+ @GeneratedValue
+ @DocumentId
+ private Long id;
+
+ @Field( index = Index.TOKENIZED )
+ private String name;
+
+ @OneToOne( cascade = CascadeType.PERSIST )
+ @IndexedEmbedded
+ private Address address;
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+}
17 years, 1 month
Hibernate SVN: r11259 - in trunk/Hibernate3: src/org/hibernate/id and 6 other directories.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2007-03-07 17:55:12 -0500 (Wed, 07 Mar 2007)
New Revision: 11259
Added:
trunk/Hibernate3/src/org/hibernate/id/enhanced/
trunk/Hibernate3/src/org/hibernate/id/enhanced/AccessCallback.java
trunk/Hibernate3/src/org/hibernate/id/enhanced/DatabaseStructure.java
trunk/Hibernate3/src/org/hibernate/id/enhanced/Optimizer.java
trunk/Hibernate3/src/org/hibernate/id/enhanced/OptimizerFactory.java
trunk/Hibernate3/src/org/hibernate/id/enhanced/SegmentedTableGenerator.java
trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStructure.java
trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java
trunk/Hibernate3/src/org/hibernate/id/enhanced/TableStructure.java
trunk/Hibernate3/test/org/hibernate/test/idgen/
trunk/Hibernate3/test/org/hibernate/test/idgen/IdGenSuite.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Entity.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Entity.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java
Modified:
trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java
trunk/Hibernate3/src/org/hibernate/dialect/H2Dialect.java
trunk/Hibernate3/src/org/hibernate/dialect/HSQLDialect.java
trunk/Hibernate3/src/org/hibernate/dialect/Oracle9Dialect.java
Log:
HHH-2471 : enhanced id generators
Modified: trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2007-03-07 22:41:22 UTC (rev 11258)
+++ trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -570,6 +570,18 @@
}
/**
+ * Does this dialect support "pooled" sequences. Not aware of a better
+ * name for this. Essentially can we specify the initial and increment values?
+ *
+ * @return True if such "pooled" sequences are supported; false otherwise.
+ * @see #getCreateSequenceStrings(String, int, int)
+ * @see #getCreateSequenceString(String, int, int)
+ */
+ public boolean supportsPooledSequences() {
+ return false;
+ }
+
+ /**
* Generate the appropriate select statement to to retreive the next value
* of a sequence.
* <p/>
@@ -604,12 +616,26 @@
* @param sequenceName The name of the sequence
* @return The sequence creation commands
* @throws MappingException If sequences are not supported.
+ * @deprecated Use {@link #getCreateSequenceString(String, int, int)} instead
*/
public String[] getCreateSequenceStrings(String sequenceName) throws MappingException {
return new String[] { getCreateSequenceString( sequenceName ) };
}
/**
+ * An optional multi-line form for databases which {@link #supportsPooledSequences()}.
+ *
+ * @param sequenceName The name of the sequence
+ * @param initialValue The initial value to apply to 'create sequence' statement
+ * @param incrementSize The increment value to apply to 'create sequence' statement
+ * @return The sequence creation commands
+ * @throws MappingException If sequences are not supported.
+ */
+ public String[] getCreateSequenceStrings(String sequenceName, int initialValue, int incrementSize) throws MappingException {
+ return new String[] { getCreateSequenceString( sequenceName, initialValue, incrementSize ) };
+ }
+
+ /**
* Typically dialects which support sequences can create a sequence
* with a single command. This is convenience form of
* {@link #getCreateSequenceStrings} to help facilitate that.
@@ -628,6 +654,30 @@
}
/**
+ * Overloaded form of {@link #getCreateSequenceString(String)}, additionally
+ * taking the initial value and increment size to be applied to the sequence
+ * definition.
+ * </p>
+ * The default definition is to suffix {@link #getCreateSequenceString(String)}
+ * with the string: " start with {initialValue} increment by {incrementSize}" where
+ * {initialValue} and {incrementSize} are replacement placeholders. Generally
+ * dialects should only need to override this method if different key phrases
+ * are used to apply the allocation information.
+ *
+ * @param sequenceName The name of the sequence
+ * @param initialValue The initial value to apply to 'create sequence' statement
+ * @param incrementSize The increment value to apply to 'create sequence' statement
+ * @return The sequence creation command
+ * @throws MappingException If sequences are not supported.
+ */
+ protected String getCreateSequenceString(String sequenceName, int initialValue, int incrementSize) throws MappingException {
+ if ( supportsPooledSequences() ) {
+ return getCreateSequenceString( sequenceName ) + " start with " + initialValue + " increment by " + incrementSize;
+ }
+ throw new MappingException( "Dialect does not support pooled sequences" );
+ }
+
+ /**
* The multiline script used to drop a sequence.
*
* @param sequenceName The name of the sequence
@@ -1648,8 +1698,8 @@
/**
* Does the dialect support an exists statement in the select clause?
- *
- * @return
+ *
+ * @return True if exists checks are allowed in the select clause; false otherwise.
*/
public boolean supportsExistsInSelect() {
return true;
Modified: trunk/Hibernate3/src/org/hibernate/dialect/H2Dialect.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/H2Dialect.java 2007-03-07 22:41:22 UTC (rev 11258)
+++ trunk/Hibernate3/src/org/hibernate/dialect/H2Dialect.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -237,7 +237,11 @@
return true;
}
- public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
+ public boolean supportsPooledSequences() {
+ return true;
+ }
+
+ public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
return EXTRACTER;
}
Modified: trunk/Hibernate3/src/org/hibernate/dialect/HSQLDialect.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/HSQLDialect.java 2007-03-07 22:41:22 UTC (rev 11258)
+++ trunk/Hibernate3/src/org/hibernate/dialect/HSQLDialect.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -194,11 +194,23 @@
return false;
}
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public boolean supportsPooledSequences() {
+ return true;
+ }
+
public String[] getCreateSequenceStrings(String sequenceName) {
+ return getCreateSequenceStrings( sequenceName, 1, 1 );
+ }
+
+ public String[] getCreateSequenceStrings(String sequenceName, int initialValue, int incrementSize) {
return new String[] {
"create table dual_" + sequenceName + " (zero integer)",
"insert into dual_" + sequenceName + " values (0)",
- "create sequence " + sequenceName + " start with 1"
+ "create sequence " + sequenceName + " start with " + initialValue + " increment by " + incrementSize
};
}
@@ -226,10 +238,6 @@
}
}
- public boolean supportsSequences() {
- return true;
- }
-
public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
return EXTRACTER;
}
Modified: trunk/Hibernate3/src/org/hibernate/dialect/Oracle9Dialect.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/Oracle9Dialect.java 2007-03-07 22:41:22 UTC (rev 11258)
+++ trunk/Hibernate3/src/org/hibernate/dialect/Oracle9Dialect.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -172,6 +172,10 @@
return true;
}
+ public boolean supportsPooledSequences() {
+ return true;
+ }
+
public boolean supportsLimit() {
return true;
}
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/AccessCallback.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/AccessCallback.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/AccessCallback.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,16 @@
+package org.hibernate.id.enhanced;
+
+/**
+ * Contract for providing callback access to a {@link DatabaseStructure},
+ * typically from the {@link Optimizer}.
+ *
+ * @author Steve Ebersole
+ */
+public interface AccessCallback {
+ /**
+ * Retrieve the next value from the underlying source.
+ *
+ * @return The next value.
+ */
+ public long getNextValue();
+}
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/DatabaseStructure.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/DatabaseStructure.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/DatabaseStructure.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,61 @@
+package org.hibernate.id.enhanced;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Encapsulates definition of the underlying data structure backing a
+ * sequence-style generator.
+ *
+ * @author Steve Ebersole
+ */
+public interface DatabaseStructure {
+ /**
+ * The name of the database structure (table or sequence).
+ * @return The structure name.
+ */
+ public String getName();
+
+ /**
+ * How many times has this structure been accessed through this reference?
+ * @return The number of accesses.
+ */
+ public int getTimesAccessed();
+
+ /**
+ * The configured increment size
+ * @return The configured increment size
+ */
+ public int getIncrementSize();
+
+ /**
+ * A callback to be able to get the next value from the underlying
+ * structure as needed.
+ *
+ * @param session The session.
+ * @return The next value.
+ */
+ public AccessCallback buildCallback(SessionImplementor session);
+
+ /**
+ * Prepare this structure for use. Called sometime after instantiation,
+ * but before first use.
+ *
+ * @param optimizer The optimizer being applied to the generator.
+ */
+ public void prepare(Optimizer optimizer);
+
+ /**
+ * Commands needed to create the underlying structures.
+ * @param dialect The database dialect being used.
+ * @return The creation commands.
+ */
+ public String[] sqlCreateStrings(Dialect dialect);
+
+ /**
+ * Commands needed to drop the underlying structures.
+ * @param dialect The database dialect being used.
+ * @return The drop commands.
+ */
+ public String[] sqlDropStrings(Dialect dialect);
+}
\ No newline at end of file
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/Optimizer.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/Optimizer.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/Optimizer.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,53 @@
+package org.hibernate.id.enhanced;
+
+import java.io.Serializable;
+
+/**
+ * Performs optimization on an optimizable identifier generator. Typically
+ * this optimization takes the form of trying to ensure we do not have to
+ * hit the database on each and every request to get an identifier value.
+ * <p/>
+ * Optimizers work on constructor injection. They should provide
+ * a constructor with the following arguments <ol>
+ * <li>java.lang.Class - The return type for the generated values</li>
+ * <li>int - The increment size</li>
+ * </ol>
+ *
+ * @author Steve Ebersole
+ */
+public interface Optimizer {
+ /**
+ * Generate an identifier value accounting for this specific optimization.
+ *
+ * @param callback Callback to access the underlying value source.
+ * @return The generated identifier value.
+ */
+ public Serializable generate(AccessCallback callback);
+
+ /**
+ * A common means to access the last value obtained from the underlying
+ * source. This is intended for testing purposes, since accessing the
+ * unerlying database source directly is much more difficult.
+ *
+ * @return The last value we obtained from the underlying source;
+ * -1 indicates we have not yet consulted with the source.
+ */
+ public long getLastSourceValue();
+
+ /**
+ * Retrieves the defined increment size.
+ *
+ * @return The increment size.
+ */
+ public int getIncrementSize();
+
+ /**
+ * Are increments to be applied to the values stored in the underlying
+ * value source?
+ *
+ * @return True if the values in the source are to be incremented
+ * according to the defined increment size; false otherwise, in which
+ * case the increment is totally an in memory construct.
+ */
+ public boolean applyIncrementSizeToSourceValues();
+}
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/OptimizerFactory.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/OptimizerFactory.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/OptimizerFactory.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,202 @@
+package org.hibernate.id.enhanced;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.id.IdentifierGeneratorFactory;
+
+/**
+ * Factory for {@link Optimizer} instances.
+ *
+ * @author Steve Ebersole
+ */
+public class OptimizerFactory {
+ private static final Log log = LogFactory.getLog( OptimizerFactory.class );
+
+ public static final String NONE = "none";
+ public static final String HILO = "hilo";
+ public static final String POOL = "pooled";
+
+ private static Class[] CTOR_SIG = new Class[] { Class.class, int.class };
+
+ public static Optimizer buildOptimizer(String type, Class returnClass, int incrementSize) {
+ String optimizerClassName;
+ if ( NONE.equals( type ) ) {
+ optimizerClassName = NoopOptimizer.class.getName();
+ }
+ else if ( HILO.equals( type ) ) {
+ optimizerClassName = HiLoOptimizer.class.getName();
+ }
+ else if ( POOL.equals( type ) ) {
+ optimizerClassName = PooledOptimizer.class.getName();
+ }
+ else {
+ optimizerClassName = type;
+ }
+
+ try {
+ Class optimizerClass = ReflectHelper.classForName( optimizerClassName );
+ Constructor ctor = optimizerClass.getConstructor( CTOR_SIG );
+ return ( Optimizer ) ctor.newInstance( new Object[] { returnClass, new Integer( incrementSize ) } );
+ }
+ catch( Throwable ignore ) {
+ // intentionally empty
+ }
+
+ // the default...
+ return new NoopOptimizer( returnClass, incrementSize );
+ }
+
+ public static abstract class OptimizerSupport implements Optimizer {
+ protected final Class returnClass;
+ protected final int incrementSize;
+
+ protected OptimizerSupport(Class returnClass, int incrementSize) {
+ if ( returnClass == null ) {
+ throw new HibernateException( "return class is required" );
+ }
+ this.returnClass = returnClass;
+ this.incrementSize = incrementSize;
+ }
+
+ protected Serializable make(long value) {
+ return IdentifierGeneratorFactory.createNumber( value, returnClass );
+ }
+
+ public Class getReturnClass() {
+ return returnClass;
+ }
+
+ public int getIncrementSize() {
+ return incrementSize;
+ }
+ }
+
+ public static class NoopOptimizer extends OptimizerSupport {
+ private long lastSourceValue = -1;
+
+ public NoopOptimizer(Class returnClass, int incrementSize) {
+ super( returnClass, incrementSize );
+ }
+
+ public Serializable generate(AccessCallback callback) {
+ if ( lastSourceValue == -1 ) {
+ while( lastSourceValue <= 0 ) {
+ lastSourceValue = callback.getNextValue();
+ }
+ }
+ else {
+ lastSourceValue = callback.getNextValue();
+ }
+ return make( lastSourceValue );
+ }
+
+ public long getLastSourceValue() {
+ return lastSourceValue;
+ }
+
+ public boolean applyIncrementSizeToSourceValues() {
+ return false;
+ }
+ }
+
+ public static class HiLoOptimizer extends OptimizerSupport {
+ private long lastSourceValue = -1;
+ private long value;
+ private long hiValue;
+
+ public HiLoOptimizer(Class returnClass, int incrementSize) {
+ super( returnClass, incrementSize );
+ if ( incrementSize < 1 ) {
+ throw new HibernateException( "increment size cannot be less than 1" );
+ }
+ if ( log.isTraceEnabled() ) {
+ log.trace( "creating hilo optimizer with [incrementSize=" + incrementSize + "; returnClass=" + returnClass.getName() + "]" );
+ }
+ }
+
+ public Serializable generate(AccessCallback callback) {
+ if ( lastSourceValue < 0 ) {
+ lastSourceValue = callback.getNextValue();
+ while ( lastSourceValue <= 0 ) {
+ lastSourceValue = callback.getNextValue();
+ }
+ hiValue = ( lastSourceValue * incrementSize ) + 1;
+ value = hiValue - incrementSize;
+ }
+ else if ( value >= hiValue ) {
+ lastSourceValue = callback.getNextValue();
+ hiValue = ( lastSourceValue * incrementSize ) + 1;
+ }
+ return make( value++ );
+ }
+
+
+ public long getLastSourceValue() {
+ return lastSourceValue;
+ }
+
+ public boolean applyIncrementSizeToSourceValues() {
+ return false;
+ }
+
+ public long getLastValue() {
+ return value - 1;
+ }
+
+ public long getHiValue() {
+ return hiValue;
+ }
+ }
+
+ public static class PooledOptimizer extends OptimizerSupport {
+ private long value;
+ private long hiValue = -1;
+
+ public PooledOptimizer(Class returnClass, int incrementSize) {
+ super( returnClass, incrementSize );
+ if ( incrementSize < 1 ) {
+ throw new HibernateException( "increment size cannot be less than 1" );
+ }
+ if ( log.isTraceEnabled() ) {
+ log.trace( "creating pooled optimizer with [incrementSize=" + incrementSize + "; returnClass=" + returnClass.getName() + "]" );
+ }
+ }
+
+ public Serializable generate(AccessCallback callback) {
+ if ( hiValue < 0 ) {
+ value = callback.getNextValue();
+ if ( value < 1 ) {
+ // unfortunately not really safe to normalize this
+ // to 1 as an initial value like we do the others
+ // because we would not be able to control this if
+ // we are using a sequence...
+ log.info( "pooled optimizer source reported [" + value + "] as the initial value; use of 1 or greater highly recommended" );
+ }
+ hiValue = callback.getNextValue();
+ }
+ else if ( value >= hiValue ) {
+ hiValue = callback.getNextValue();
+ value = hiValue - incrementSize;
+ }
+ return make( value++ );
+ }
+
+ public long getLastSourceValue() {
+ return hiValue;
+ }
+
+ public boolean applyIncrementSizeToSourceValues() {
+ return true;
+ }
+
+ public long getLastValue() {
+ return value - 1;
+ }
+ }
+}
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/SegmentedTableGenerator.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/SegmentedTableGenerator.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/SegmentedTableGenerator.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,304 @@
+package org.hibernate.id.enhanced;
+
+import java.sql.Types;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Properties;
+import java.util.HashMap;
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.engine.TransactionHelper;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.id.Configurable;
+import org.hibernate.type.Type;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.LockMode;
+import org.hibernate.mapping.Table;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.StringHelper;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * A "segmented" version of the enhanced table generator. The term "segmented"
+ * refers to the fact that this table can hold multiple value generators,
+ * segmented by a key.
+ * <p/>
+ * Configuration parameters:
+ * <table>
+ * <tr>
+ * <td><b>NAME</b></td>
+ * <td><b>DEFAULT</b></td>
+ * <td><b>DESCRIPTION</b></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #TABLE_PARAM}</td>
+ * <td>{@link #DEF_TABLE}</td>
+ * <td>The name of the table to use to store/retrieve values</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #VALUE_COLUMN_PARAM}</td>
+ * <td>{@link #DEF_VALUE_COLUMN}</td>
+ * <td>The name of column which holds the sequence value for the given segment</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #SEGMENT_COLUMN_PARAM}</td>
+ * <td>{@link #DEF_SEGMENT_COLUMN}</td>
+ * <td>The name of the column which holds the segment key</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #SEGMENT_VALUE_PARAM}</td>
+ * <td>{@link #DEF_SEGMENT_VALUE}</td>
+ * <td>The value indicating which segment is used by this generator; refers to values in the {@link #SEGMENT_COLUMN_PARAM} column</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #SEGMENT_LENGTH_PARAM}</td>
+ * <td>{@link #DEF_SEGMENT_LENGTH}</td>
+ * <td>The data length of the {@link #SEGMENT_COLUMN_PARAM} column; used for schema creation</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #INITIAL_PARAM}</td>
+ * <td>{@link #DEFAULT_INITIAL_VALUE}</td>
+ * <td>The initial value to be stored for the given segment</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #INCREMENT_PARAM}</td>
+ * <td>{@link #DEFAULT_INCREMENT_SIZE}</td>
+ * <td>The increment size for the underlying segment; see the discussion on {@link Optimizer} for more details.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #OPT_PARAM}</td>
+ * <td><i>depends on defined increment size</i></td>
+ * <td>Allows explicit definition of which optimization strategy to use</td>
+ * </tr>
+ * </table>
+ *
+ * @author Steve Ebersole
+ */
+public class SegmentedTableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
+ private static final Log log = LogFactory.getLog( SegmentedTableGenerator.class );
+
+ public static final String TABLE_PARAM = "table_name";
+ public static final String DEF_TABLE = "hibernate_sequences";
+
+ public static final String VALUE_COLUMN_PARAM = "value_column_name";
+ public static final String DEF_VALUE_COLUMN = "next_val";
+
+ public static final String SEGMENT_COLUMN_PARAM = "segment_column_name";
+ public static final String DEF_SEGMENT_COLUMN = "sequence_name";
+
+ public static final String SEGMENT_VALUE_PARAM = "segment_value";
+ public static final String DEF_SEGMENT_VALUE = "default";
+
+ public static final String SEGMENT_LENGTH_PARAM = "segment_value_length";
+ public static final int DEF_SEGMENT_LENGTH = 255;
+
+ public static final String INITIAL_PARAM = "initial_value";
+ public static final int DEFAULT_INITIAL_VALUE = 1;
+
+ public static final String INCREMENT_PARAM = "increment_size";
+ public static final int DEFAULT_INCREMENT_SIZE = 1;
+
+ public static final String OPT_PARAM = "optimizer";
+
+
+ private String tableName;
+ private String valueColumnName;
+ private String segmentColumnName;
+ private String segmentValue;
+ private int segmentValueLength;
+ private int initialValue;
+ private int incrementSize;
+
+ private Type identifierType;
+
+ private String query;
+ private String insert;
+ private String update;
+
+ private Optimizer optimizer;
+
+ public String getTableName() {
+ return tableName;
+ }
+
+ public String getSegmentColumnName() {
+ return segmentColumnName;
+ }
+
+ public String getSegmentValue() {
+ return segmentValue;
+ }
+
+ public int getSegmentValueLength() {
+ return segmentValueLength;
+ }
+
+ public String getValueColumnName() {
+ return valueColumnName;
+ }
+
+ public Type getIdentifierType() {
+ return identifierType;
+ }
+
+ public int getInitialValue() {
+ return initialValue;
+ }
+
+ public int getIncrementSize() {
+ return incrementSize;
+ }
+
+ public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
+ tableName = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
+ if ( tableName.indexOf( '.' ) < 0 ) {
+ String schemaName = params.getProperty( SCHEMA );
+ String catalogName = params.getProperty( CATALOG );
+ tableName = Table.qualify( catalogName, schemaName, tableName );
+ }
+
+ segmentColumnName = PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
+ segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
+ if ( StringHelper.isEmpty( segmentValue ) ) {
+ log.debug( "explicit segment value for id generator [" + tableName + '.' + segmentColumnName + "] suggested; using default [" + DEF_SEGMENT_VALUE + "]" );
+ segmentValue = DEF_SEGMENT_VALUE;
+ }
+ segmentValueLength = PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
+ valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ identifierType = type;
+
+ String query = "select " + valueColumnName +
+ " from " + tableName + " tbl" +
+ " where tbl." + segmentColumnName + "=?";
+ HashMap lockMap = new HashMap();
+ lockMap.put( "tbl", LockMode.UPGRADE );
+ this.query = dialect.applyLocksToSql( query, lockMap, CollectionHelper.EMPTY_MAP );
+
+ update = "update " + tableName +
+ " set " + valueColumnName + "=? " +
+ " where " + valueColumnName + "=? and " + segmentColumnName + "=?";
+
+ insert = "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
+
+ String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
+ String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ }
+
+ public synchronized Serializable generate(final SessionImplementor session, Object obj) {
+ return optimizer.generate(
+ new AccessCallback() {
+ public long getNextValue() {
+ return ( ( Number ) doWorkInNewTransaction( session ) ).longValue();
+ }
+ }
+ );
+ }
+
+ public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
+ int result;
+ int rows;
+ do {
+ sql = query;
+ SQL.debug( sql );
+ PreparedStatement queryPS = conn.prepareStatement( query );
+ try {
+ queryPS.setString( 1, segmentValue );
+ ResultSet queryRS = queryPS.executeQuery();
+ if ( !queryRS.next() ) {
+ PreparedStatement insertPS = null;
+ try {
+ result = initialValue;
+ sql = insert;
+ SQL.debug( sql );
+ insertPS = conn.prepareStatement( insert );
+ insertPS.setString( 1, segmentValue );
+ insertPS.setLong( 2, result );
+ insertPS.execute();
+ }
+ finally {
+ if ( insertPS != null ) {
+ insertPS.close();
+ }
+ }
+ }
+ else {
+ result = queryRS.getInt( 1 );
+ }
+ queryRS.close();
+ }
+ catch ( SQLException sqle ) {
+ log.error( "could not read or init a hi value", sqle );
+ throw sqle;
+ }
+ finally {
+ queryPS.close();
+ }
+
+ sql = update;
+ SQL.debug( sql );
+ PreparedStatement updatePS = conn.prepareStatement( update );
+ try {
+ long newValue = optimizer.applyIncrementSizeToSourceValues()
+ ? result + incrementSize : result + 1;
+ updatePS.setLong( 1, newValue );
+ updatePS.setLong( 2, result );
+ updatePS.setString( 3, segmentValue );
+ rows = updatePS.executeUpdate();
+ }
+ catch ( SQLException sqle ) {
+ log.error( "could not update hi value in: " + tableName, sqle );
+ throw sqle;
+ }
+ finally {
+ updatePS.close();
+ }
+ }
+ while ( rows == 0 );
+ return new Integer( result );
+ }
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ return new String[] {
+ new StringBuffer()
+ .append( "create table " )
+ .append( tableName )
+ .append( " ( " )
+ .append( segmentColumnName )
+ .append( " " )
+ .append( dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 ) )
+ .append( ", " )
+ .append( valueColumnName )
+ .append( " " )
+ .append( dialect.getTypeName( Types.BIGINT ) )
+ .append( " ) " )
+ .toString()
+ };
+ }
+
+ public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
+ StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
+ if ( dialect.supportsIfExistsBeforeTableName() ) {
+ sqlDropString.append( "if exists " );
+ }
+ sqlDropString.append( tableName ).append( dialect.getCascadeConstraintsString() );
+ if ( dialect.supportsIfExistsAfterTableName() ) {
+ sqlDropString.append( " if exists" );
+ }
+ return new String[] { sqlDropString.toString() };
+ }
+
+ public Object generatorKey() {
+ return tableName;
+ }
+}
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStructure.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStructure.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStructure.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,103 @@
+package org.hibernate.id.enhanced;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.HibernateException;
+
+/**
+ * Describes a sequence.
+ *
+ * @author Steve Ebersole
+ */
+public class SequenceStructure implements DatabaseStructure {
+ private static final Log log = LogFactory.getLog( SequenceStructure.class );
+
+ private final String sequenceName;
+ private final int initialValue;
+ private final int incrementSize;
+ private final String sql;
+ private boolean applyIncrementSizeToSourceValues;
+ private int accessCounter;
+
+ public SequenceStructure(Dialect dialect, String sequenceName, int initialValue, int incrementSize) {
+ this.sequenceName = sequenceName;
+ this.initialValue = initialValue;
+ this.incrementSize = incrementSize;
+ sql = dialect.getSequenceNextValString( sequenceName );
+ }
+
+ public String getName() {
+ return sequenceName;
+ }
+
+ public int getIncrementSize() {
+ return incrementSize;
+ }
+
+ public int getTimesAccessed() {
+ return accessCounter;
+ }
+
+ public AccessCallback buildCallback(final SessionImplementor session) {
+ return new AccessCallback() {
+ public long getNextValue() {
+ accessCounter++;
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
+ try {
+ ResultSet rs = st.executeQuery();
+ try {
+ rs.next();
+ long result = rs.getLong( 1 );
+ if ( log.isDebugEnabled() ) {
+ log.debug("Sequence identifier generated: " + result);
+ }
+ return result;
+ }
+ finally {
+ try {
+ rs.close();
+ }
+ catch( Throwable ignore ) {
+ // intentionally empty
+ }
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+
+ }
+ catch ( SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not get next sequence value",
+ sql
+ );
+ }
+ }
+ };
+ }
+
+ public void prepare(Optimizer optimizer) {
+ applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
+ }
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
+ return dialect.getCreateSequenceStrings( sequenceName, initialValue, sourceIncrementSize );
+ }
+
+ public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
+ return dialect.getDropSequenceStrings( sequenceName );
+ }
+}
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,175 @@
+package org.hibernate.id.enhanced;
+
+import java.util.Properties;
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.id.Configurable;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.mapping.Table;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.type.Type;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Generates identifier values based on an sequence-style database structure.
+ * Variations range from actually using a sequence to using a table to mimic
+ * a sequence. These variations are encapsulated by the {@link DatabaseStructure}
+ * interface internally.
+ * <p/>
+ * General configuration parameters:
+ * <table>
+ * <tr>
+ * <td><b>NAME</b></td>
+ * <td><b>DEFAULT</b></td>
+ * <td><b>DESCRIPTION</b></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #SEQUENCE_PARAM}</td>
+ * <td>{@link #DEF_SEQUENCE_NAME}</td>
+ * <td>The name of the sequence/table to use to store/retrieve values</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #INITIAL_PARAM}</td>
+ * <td>{@link #DEFAULT_INITIAL_VALUE}</td>
+ * <td>The initial value to be stored for the given segment; the effect in terms of storage varies based on {@link Optimizer} and {@link DatabaseStructure}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #INCREMENT_PARAM}</td>
+ * <td>{@link #DEFAULT_INCREMENT_SIZE}</td>
+ * <td>The increment size for the underlying segment; the effect in terms of storage varies based on {@link Optimizer} and {@link DatabaseStructure}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #OPT_PARAM}</td>
+ * <td><i>depends on defined increment size</i></td>
+ * <td>Allows explicit definition of which optimization strategy to use</td>
+ * </tr>
+ * <td>{@link #FORCE_TBL_PARAM}</td>
+ * <td><b><i>false<i/></b></td>
+ * <td>Allows explicit definition of which optimization strategy to use</td>
+ * </tr>
+ * </table>
+ * <p/>
+ * Configuration parameters used specifically when the underlying structure is a table:
+ * <table>
+ * <tr>
+ * <td><b>NAME</b></td>
+ * <td><b>DEFAULT</b></td>
+ * <td><b>DESCRIPTION</b></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #VALUE_COLUMN_PARAM}</td>
+ * <td>{@link #DEF_VALUE_COLUMN}</td>
+ * <td>The name of column which holds the sequence value for the given segment</td>
+ * </tr>
+ * </table>
+ *
+ * @author Steve Ebersole
+ */
+public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Configurable {
+ private static final Log log = LogFactory.getLog( SequenceStyleGenerator.class );
+
+ // general purpose parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ public static final String SEQUENCE_PARAM = "sequence_name";
+ public static final String DEF_SEQUENCE_NAME = "hibernate_sequence";
+
+ public static final String INITIAL_PARAM = "initial_value";
+ public static final int DEFAULT_INITIAL_VALUE = 1;
+
+ public static final String INCREMENT_PARAM = "increment_size";
+ public static final int DEFAULT_INCREMENT_SIZE = 1;
+
+ public static final String OPT_PARAM = "optimizer";
+
+ public static final String FORCE_TBL_PARAM = "force_table_use";
+
+
+ // table-specific parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ public static final String VALUE_COLUMN_PARAM = "value_column";
+ public static final String DEF_VALUE_COLUMN = "next_val";
+
+
+ // state ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ private DatabaseStructure databaseStructure;
+ private Optimizer optimizer;
+ private Type identifierType;
+
+ public DatabaseStructure getDatabaseStructure() {
+ return databaseStructure;
+ }
+
+ public Optimizer getOptimizer() {
+ return optimizer;
+ }
+
+ public Type getIdentifierType() {
+ return identifierType;
+ }
+
+
+ // Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
+ identifierType = type;
+ boolean forceTableUse = PropertiesHelper.getBoolean( FORCE_TBL_PARAM, params, false );
+
+ String sequenceName = PropertiesHelper.getString( SEQUENCE_PARAM, params, DEF_SEQUENCE_NAME );
+ if ( sequenceName.indexOf( '.' ) < 0 ) {
+ String schemaName = params.getProperty( SCHEMA );
+ String catalogName = params.getProperty( CATALOG );
+ sequenceName = Table.qualify( catalogName, schemaName, sequenceName );
+ }
+ int initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ int incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+
+ String valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+
+ String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
+ String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ if ( OptimizerFactory.NONE.equals( optimizationStrategy ) && incrementSize > 1 ) {
+ log.warn( "config specified explicit optimizer of [" + OptimizerFactory.NONE + "], but [" + INCREMENT_PARAM + "=" + incrementSize + "; honoring optimizer setting" );
+ incrementSize = 1;
+ }
+ if ( dialect.supportsSequences() && !forceTableUse ) {
+ if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
+ // TODO : may even be better to fall back to a pooled table strategy here so that the db stored values remain consistent...
+ optimizationStrategy = OptimizerFactory.HILO;
+ }
+ databaseStructure = new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
+ }
+ else {
+ databaseStructure = new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
+ }
+
+ optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ databaseStructure.prepare( optimizer );
+ }
+
+
+ // IdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
+ return optimizer.generate( databaseStructure.buildCallback( session ) );
+ }
+
+
+ // PersistentIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Object generatorKey() {
+ return databaseStructure.getName();
+ }
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ return databaseStructure.sqlCreateStrings( dialect );
+ }
+
+ public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
+ return databaseStructure.sqlDropStrings( dialect );
+ }
+
+}
Added: trunk/Hibernate3/src/org/hibernate/id/enhanced/TableStructure.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/enhanced/TableStructure.java (rev 0)
+++ trunk/Hibernate3/src/org/hibernate/id/enhanced/TableStructure.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,144 @@
+package org.hibernate.id.enhanced;
+
+import java.sql.Types;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.LockMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TransactionHelper;
+import org.hibernate.id.IdentifierGenerationException;
+
+/**
+ * Describes a table used to mimic sequence behavior
+ *
+ * @author Steve Ebersole
+ */
+public class TableStructure extends TransactionHelper implements DatabaseStructure {
+ private static final Log log = LogFactory.getLog( TableStructure.class );
+ private static final Log SQL_LOG = LogFactory.getLog( "org.hibernate.SQL" );
+
+ private final String tableName;
+ private final String valueColumnName;
+ private final int initialValue;
+ private final int incrementSize;
+ private final String select;
+ private final String update;
+ private boolean applyIncrementSizeToSourceValues;
+ private int accessCounter;
+
+ public TableStructure(Dialect dialect, String tableName, String valueColumnName, int initialValue, int incrementSize) {
+ this.tableName = tableName;
+ this.initialValue = initialValue;
+ this.incrementSize = incrementSize;
+ this.valueColumnName = valueColumnName;
+
+ select = "select " + valueColumnName + " id_val" +
+ " from " + dialect.appendLockHint( LockMode.UPGRADE, tableName ) +
+ dialect.getForUpdateString();
+
+ update = "update " + tableName +
+ " set " + valueColumnName + "= ?" +
+ " where " + valueColumnName + "=?";
+ }
+
+ public String getName() {
+ return tableName;
+ }
+
+ public int getIncrementSize() {
+ return incrementSize;
+ }
+
+ public int getTimesAccessed() {
+ return accessCounter;
+ }
+
+ public void prepare(Optimizer optimizer) {
+ applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
+ }
+
+ public AccessCallback buildCallback(final SessionImplementor session) {
+ return new AccessCallback() {
+ public long getNextValue() {
+ return ( ( Number ) doWorkInNewTransaction( session ) ).longValue();
+ }
+ };
+ }
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ return new String[] {
+ "create table " + tableName + " ( " + valueColumnName + " " + dialect.getTypeName( Types.BIGINT ) + " )",
+ "insert into " + tableName + " values ( " + initialValue + " )"
+ };
+ }
+
+ public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
+ StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
+ if ( dialect.supportsIfExistsBeforeTableName() ) {
+ sqlDropString.append( "if exists " );
+ }
+ sqlDropString.append( tableName ).append( dialect.getCascadeConstraintsString() );
+ if ( dialect.supportsIfExistsAfterTableName() ) {
+ sqlDropString.append( " if exists" );
+ }
+ return new String[] { sqlDropString.toString() };
+ }
+
+ protected Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
+ long result;
+ int rows;
+ do {
+ sql = select;
+ SQL_LOG.debug( sql );
+ PreparedStatement qps = conn.prepareStatement( select );
+ try {
+ ResultSet rs = qps.executeQuery();
+ if ( !rs.next() ) {
+ String err = "could not read a hi value - you need to populate the table: " + tableName;
+ log.error( err );
+ throw new IdentifierGenerationException( err );
+ }
+ result = rs.getLong( 1 );
+ rs.close();
+ }
+ catch ( SQLException sqle ) {
+ log.error( "could not read a hi value", sqle );
+ throw sqle;
+ }
+ finally {
+ qps.close();
+ }
+
+ sql = update;
+ SQL_LOG.debug( sql );
+ PreparedStatement ups = conn.prepareStatement( update );
+ try {
+ int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
+ ups.setLong( 1, result + increment );
+ ups.setLong( 2, result );
+ rows = ups.executeUpdate();
+ }
+ catch ( SQLException sqle ) {
+ log.error( "could not update hi value in: " + tableName, sqle );
+ throw sqle;
+ }
+ finally {
+ ups.close();
+ }
+ } while ( rows == 0 );
+
+ accessCounter++;
+
+ return new Long( result );
+ }
+
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/IdGenSuite.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/IdGenSuite.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/IdGenSuite.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,37 @@
+package org.hibernate.test.idgen;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.idgen.enhanced.sequence.BasicSequenceTest;
+import org.hibernate.test.idgen.enhanced.sequence.HiLoSequenceTest;
+import org.hibernate.test.idgen.enhanced.sequence.PooledSequenceTest;
+import org.hibernate.test.idgen.enhanced.table.BasicTableTest;
+import org.hibernate.test.idgen.enhanced.table.HiLoTableTest;
+import org.hibernate.test.idgen.enhanced.table.PooledTableTest;
+import org.hibernate.test.idgen.enhanced.OptimizerUnitTest;
+import org.hibernate.test.idgen.enhanced.SequenceStyleConfigUnitTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class IdGenSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "id generators" );
+
+ suite.addTest( OptimizerUnitTest.suite() );
+ suite.addTest( SequenceStyleConfigUnitTest.suite() );
+
+ suite.addTest( BasicSequenceTest.suite() );
+ suite.addTest( HiLoSequenceTest.suite() );
+ suite.addTest( PooledSequenceTest.suite() );
+
+ suite.addTest( BasicTableTest.suite() );
+ suite.addTest( HiLoTableTest.suite() );
+ suite.addTest( PooledTableTest.suite() );
+
+ return suite;
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,154 @@
+package org.hibernate.test.idgen.enhanced;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.id.enhanced.Optimizer;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.AccessCallback;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class OptimizerUnitTest extends UnitTestCase {
+ public OptimizerUnitTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new TestSuite( OptimizerUnitTest.class );
+ }
+
+ public void testBasicNoOptimizerUsage() {
+ // test historic sequence behavior, where the initial values start at 1...
+ SourceMock sequence = new SourceMock( 1 );
+ Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
+ for ( int i = 1; i < 11; i++ ) {
+ final Long next = ( Long ) optimizer.generate( sequence );
+ assertEquals( i, next.intValue() );
+ }
+ assertEquals( 10, sequence.getTimesCalled() );
+ assertEquals( 10, sequence.getCurrentValue() );
+
+ // test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
+ sequence = new SourceMock( 0 );
+ optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
+ for ( int i = 1; i < 11; i++ ) {
+ final Long next = ( Long ) optimizer.generate( sequence );
+ assertEquals( i, next.intValue() );
+ }
+ assertEquals( 11, sequence.getTimesCalled() ); // an extra time to get to 1 initially
+ assertEquals( 10, sequence.getCurrentValue() );
+ }
+
+ public void testBasicHiLoOptimizerUsage() {
+ int increment = 10;
+ Long next;
+
+ // test historic sequence behavior, where the initial values start at 1...
+ SourceMock sequence = new SourceMock( 1 );
+ Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
+ for ( int i = 1; i <= increment; i++ ) {
+ next = ( Long ) optimizer.generate( sequence );
+ assertEquals( i, next.intValue() );
+ }
+ assertEquals( 1, sequence.getTimesCalled() ); // once to initialze state
+ assertEquals( 1, sequence.getCurrentValue() );
+ // force a "clock over"
+ next = ( Long ) optimizer.generate( sequence );
+ assertEquals( 11, next.intValue() );
+ assertEquals( 2, sequence.getTimesCalled() );
+ assertEquals( 2, sequence.getCurrentValue() );
+
+ // test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
+ sequence = new SourceMock( 0 );
+ optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
+ for ( int i = 1; i <= increment; i++ ) {
+ next = ( Long ) optimizer.generate( sequence );
+ assertEquals( i, next.intValue() );
+ }
+ assertEquals( 2, sequence.getTimesCalled() ); // here have have an extra call to get to 1 initially
+ assertEquals( 1, sequence.getCurrentValue() );
+ // force a "clock over"
+ next = ( Long ) optimizer.generate( sequence );
+ assertEquals( 11, next.intValue() );
+ assertEquals( 3, sequence.getTimesCalled() );
+ assertEquals( 2, sequence.getCurrentValue() );
+ }
+
+ public void testBasicPooledOptimizerUsage() {
+ Long next;
+ // test historic sequence behavior, where the initial values start at 1...
+ SourceMock sequence = new SourceMock( 1, 10 );
+ Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 10 );
+ for ( int i = 1; i < 11; i++ ) {
+ next = ( Long ) optimizer.generate( sequence );
+ assertEquals( i, next.intValue() );
+ }
+ assertEquals( 2, sequence.getTimesCalled() ); // twice to initialze state
+ assertEquals( 11, sequence.getCurrentValue() );
+ // force a "clock over"
+ next = ( Long ) optimizer.generate( sequence );
+ assertEquals( 11, next.intValue() );
+ assertEquals( 3, sequence.getTimesCalled() );
+ assertEquals( 21, sequence.getCurrentValue() );
+ }
+
+ private static class SourceMock implements AccessCallback {
+ private long value;
+ private int increment;
+ private int timesCalled = 0;
+
+ public SourceMock(long initialValue) {
+ this( initialValue, 1 );
+ }
+
+ public SourceMock(long initialValue, int increment) {
+ this.increment = increment;
+ this.value = initialValue - increment;
+ }
+
+ public long getNextValue() {
+ timesCalled++;
+ return ( value += increment );
+ }
+
+ public int getTimesCalled() {
+ return timesCalled;
+ }
+
+ public long getCurrentValue() {
+ return value;
+ }
+ }
+
+// public void testNoopDumping() {
+// SourceMock sequence = new SourceMock( 1 );
+// Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
+// for ( int i = 1; i <= 41; i++ ) {
+// System.out.println( i + " => " + optimizer.generate( sequence ) + " (" + sequence.getCurrentValue() + ")" );
+// }
+// }
+//
+// public void testHiLoDumping() {
+// int increment = 10;
+// SourceMock sequence = new SourceMock( 1 );
+// Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
+// for ( int i = 1; i <= 41; i++ ) {
+// System.out.println( i + " => " + optimizer.generate( sequence ) + " (" + sequence.getCurrentValue() + ")" );
+// }
+// }
+//
+// public void testPooledDumping() {
+// int increment = 10;
+// SourceMock sequence = new SourceMock( 1, increment );
+// Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, increment );
+// for ( int i = 1; i <= 41; i++ ) {
+// System.out.println( i + " => " + optimizer.generate( sequence ) + " (" + sequence.getCurrentValue() + ")" );
+// }
+// }
+
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,180 @@
+package org.hibernate.test.idgen.enhanced;
+
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.id.enhanced.SequenceStyleGenerator;
+import org.hibernate.id.enhanced.SequenceStructure;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.TableStructure;
+import org.hibernate.Hibernate;
+import org.hibernate.MappingException;
+
+/**
+ * Tests that SequenceStyleGenerator configures itself as expected
+ * in various scenarios
+ *
+ * @author Steve Ebersole
+ */
+public class SequenceStyleConfigUnitTest extends UnitTestCase {
+ public SequenceStyleConfigUnitTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new TestSuite( SequenceStyleConfigUnitTest.class );
+ }
+
+ /**
+ * Test all params defaulted with a dialect supporting sequences
+ */
+ public void testDefaultedSequenceBackedConfiguration() {
+ Dialect dialect = new SequenceDialect();
+ Properties props = new Properties();
+ SequenceStyleGenerator generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+
+ assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
+ }
+
+ /**
+ * Test all params defaulted with a dialect which does not support sequences
+ */
+ public void testDefaultedTableBackedConfiguration() {
+ Dialect dialect = new TableDialect();
+ Properties props = new Properties();
+ SequenceStyleGenerator generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+
+ assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
+ }
+
+ /**
+ * Test default optimizer selection for sequence backed generators
+ * based on the configured increment size; both in the case of the
+ * dialect supporting pooled sequences (pooled) and not (hilo)
+ */
+ public void testDefaultOptimizerBasedOnIncrementBackedBySequence() {
+ Properties props = new Properties();
+ props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "10" );
+
+ // for dialects which do not support pooled sequences, we default to hilo
+ Dialect dialect = new SequenceDialect();
+ SequenceStyleGenerator generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+ assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
+
+ // for dialects which do support pooled sequences, we default to pooled
+ dialect = new PooledSequenceDialect();
+ generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+ assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
+ }
+
+ /**
+ * Test default optimizer selection for table backed generators
+ * based on the configured increment size. Here we always prefer
+ * pooled.
+ */
+ public void testDefaultOptimizerBasedOnIncrementBackedByTable() {
+ Properties props = new Properties();
+ props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "10" );
+ Dialect dialect = new TableDialect();
+ SequenceStyleGenerator generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+ assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
+ }
+
+ /**
+ * Test forcing of table as backing strucuture with dialect supporting sequences
+ */
+ public void testForceTableUse() {
+ Dialect dialect = new SequenceDialect();
+ Properties props = new Properties();
+ props.setProperty( SequenceStyleGenerator.FORCE_TBL_PARAM, "true" );
+ SequenceStyleGenerator generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+ assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
+ }
+
+ /**
+ * Test explicitly specifying both optimizer and increment
+ */
+ public void testExplicitOptimizerWithExplicitIncrementSize() {
+ // with sequence ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Dialect dialect = new SequenceDialect();
+
+ // optimizer=none w/ increment > 1 => should honor optimizer
+ Properties props = new Properties();
+ props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.NONE );
+ props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
+ SequenceStyleGenerator generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+ assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( 1, generator.getOptimizer().getIncrementSize() );
+ assertEquals( 1, generator.getDatabaseStructure().getIncrementSize() );
+
+ // optimizer=hilo w/ increment > 1 => hilo
+ props = new Properties();
+ props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.HILO );
+ props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+ assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( 20, generator.getOptimizer().getIncrementSize() );
+ assertEquals( 20, generator.getDatabaseStructure().getIncrementSize() );
+
+ // optimizer=pooled w/ increment > 1 => hilo
+ props = new Properties();
+ props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.POOL );
+ props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
+ generator = new SequenceStyleGenerator();
+ generator.configure( Hibernate.LONG, props, dialect );
+ assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
+ assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
+ assertEquals( 20, generator.getOptimizer().getIncrementSize() );
+ assertEquals( 20, generator.getDatabaseStructure().getIncrementSize() );
+
+ }
+
+ private static class TableDialect extends Dialect {
+ public boolean supportsSequences() {
+ return false;
+ }
+ }
+
+ private static class SequenceDialect extends Dialect {
+ public boolean supportsSequences() {
+ return true;
+ }
+ public boolean supportsPooledSequences() {
+ return false;
+ }
+ public String getSequenceNextValString(String sequenceName) throws MappingException {
+ return "";
+ }
+ }
+
+ private static class PooledSequenceDialect extends SequenceDialect {
+ public boolean supportsPooledSequences() {
+ return true;
+ }
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Demonstrates use of the enhanced sequence-based identifier
+ generator, with no performance optimizations (the DB is hit
+ everytime when generating a value).
+-->
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.sequence">
+
+ <class name="Entity" table="ID_SEQ_BSC_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
+ <param name="sequence_name">ID_SEQ_BSC_SEQ</param>
+ <param name="initial_value">1</param>
+ <param name="increment_size">1</param>
+ <param name="optimizer">none</param>
+ </generator>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,57 @@
+package org.hibernate.test.idgen.enhanced.sequence;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.Session;
+import org.hibernate.id.enhanced.SequenceStyleGenerator;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class BasicSequenceTest extends FunctionalTestCase {
+ public BasicSequenceTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/sequence/Basic.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BasicSequenceTest.class );
+ }
+
+ public void testNormalBoundary() {
+ EntityPersister persister = sfi().getEntityPersister( Entity.class.getName() );
+ assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
+ SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
+
+ int count = 5;
+ Entity[] entities = new Entity[count];
+ Session s = openSession();
+ s.beginTransaction();
+ for ( int i = 0; i < count; i++ ) {
+ entities[i] = new Entity( "" + ( i + 1 ) );
+ s.save( entities[i] );
+ long expectedId = i + 1;
+ assertEquals( expectedId, entities[i].getId().longValue() );
+ assertEquals( expectedId, generator.getDatabaseStructure().getTimesAccessed() );
+ assertEquals( expectedId, generator.getOptimizer().getLastSourceValue() );
+ }
+ s.getTransaction().commit();
+
+ s.beginTransaction();
+ for ( int i = 0; i < count; i++ ) {
+ assertEquals( i + 1, entities[i].getId().intValue() );
+ s.delete( entities[i] );
+ }
+ s.getTransaction().commit();
+ s.close();
+
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Entity.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Entity.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Entity.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,34 @@
+package org.hibernate.test.idgen.enhanced.sequence;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Entity {
+ private Long id;
+ private String name;
+
+ public Entity() {
+ }
+
+ public Entity(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Demonstrates use of the enhanced sequence-based identifier
+ generator, using a hilo algorithm as the optimization (to
+ avoid hitting the database to generate each value).
+-->
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.sequence">
+
+ <class name="Entity" table="ID_SEQ_HILO_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
+ <param name="sequence_name">ID_SEQ_HILO_SEQ</param>
+ <param name="initial_value">1</param>
+ <param name="increment_size">10</param>
+ <param name="optimizer">hilo</param>
+ </generator>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,67 @@
+package org.hibernate.test.idgen.enhanced.sequence;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.Session;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.SequenceStyleGenerator;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class HiLoSequenceTest extends FunctionalTestCase {
+ public HiLoSequenceTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/sequence/HiLo.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( HiLoSequenceTest.class );
+ }
+
+ public void testNormalBoundary() {
+ EntityPersister persister = sfi().getEntityPersister( Entity.class.getName() );
+ assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
+ SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
+ assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
+ OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer();
+
+ int increment = optimizer.getIncrementSize();
+ Entity[] entities = new Entity[ increment + 1 ];
+ Session s = openSession();
+ s.beginTransaction();
+ for ( int i = 0; i < increment; i++ ) {
+ entities[i] = new Entity( "" + ( i + 1 ) );
+ s.save( entities[i] );
+ assertEquals( 1, generator.getDatabaseStructure().getTimesAccessed() ); // initialization
+ assertEquals( 1, optimizer.getLastSourceValue() ); // initialization
+ assertEquals( i + 1, optimizer.getLastValue() );
+ assertEquals( increment + 1, optimizer.getHiValue() );
+ }
+ // now force a "clock over"
+ entities[ increment ] = new Entity( "" + increment );
+ s.save( entities[ increment ] );
+ assertEquals( 2, generator.getDatabaseStructure().getTimesAccessed() ); // initialization
+ assertEquals( 2, optimizer.getLastSourceValue() ); // initialization
+ assertEquals( increment + 1, optimizer.getLastValue() );
+ assertEquals( ( increment * 2 ) + 1, optimizer.getHiValue() );
+
+ s.getTransaction().commit();
+
+ s.beginTransaction();
+ for ( int i = 0; i < entities.length; i++ ) {
+ assertEquals( i + 1, entities[i].getId().intValue() );
+ s.delete( entities[i] );
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Demonstrates use of the enhanced sequence-based identifier
+ generator, using a pooled algorithm as the optimization (to
+ avoid hitting the database to generate each value).
+-->
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.sequence">
+
+ <class name="Entity" table="ID_SEQ_POOL_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
+ <param name="sequence_name">ID_SEQ_POOL_SEQ</param>
+ <param name="initial_value">1</param>
+ <param name="increment_size">10</param>
+ <param name="optimizer">pooled</param>
+ </generator>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,65 @@
+package org.hibernate.test.idgen.enhanced.sequence;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.SequenceStyleGenerator;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PooledSequenceTest extends FunctionalTestCase {
+ public PooledSequenceTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/sequence/Pooled.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PooledSequenceTest.class );
+ }
+
+ public void testNormalBoundary() {
+ EntityPersister persister = sfi().getEntityPersister( Entity.class.getName() );
+ assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
+ SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
+ assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
+ OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer();
+
+ int increment = optimizer.getIncrementSize();
+ Entity[] entities = new Entity[ increment + 1 ];
+ Session s = openSession();
+ s.beginTransaction();
+ for ( int i = 0; i < increment; i++ ) {
+ entities[i] = new Entity( "" + ( i + 1 ) );
+ s.save( entities[i] );
+ assertEquals( 2, generator.getDatabaseStructure().getTimesAccessed() ); // initialization calls seq twice
+ assertEquals( increment + 1, optimizer.getLastSourceValue() ); // initialization calls seq twice
+ assertEquals( i + 1, optimizer.getLastValue() );
+ assertEquals( increment + 1, optimizer.getLastSourceValue() );
+ }
+ // now force a "clock over"
+ entities[ increment ] = new Entity( "" + increment );
+ s.save( entities[ increment ] );
+ assertEquals( 3, generator.getDatabaseStructure().getTimesAccessed() ); // initialization (2) + clock over
+ assertEquals( ( increment * 2 ) + 1, optimizer.getLastSourceValue() ); // initialization (2) + clock over
+ assertEquals( increment + 1, optimizer.getLastValue() );
+ s.getTransaction().commit();
+
+ s.beginTransaction();
+ for ( int i = 0; i < entities.length; i++ ) {
+ assertEquals( i + 1, entities[i].getId().intValue() );
+ s.delete( entities[i] );
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Demonstrates use of the enhanced sequence-based identifier
+ generator, with no performance optimizations (the DB is hit
+ everytime when generating a value).
+-->
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.table">
+
+ <class name="Entity" table="ID_SEQ_TBL_BSC_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
+ <param name="table_name">ID_SEQ_TBL_BSC_SEQ</param>
+ <param name="value_column">hi_val</param>
+ <param name="initial_value">1</param>
+ <param name="increment_size">1</param>
+ <param name="optimizer">none</param>
+ <param name="force_tbl_use">true</param>
+ </generator>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,57 @@
+package org.hibernate.test.idgen.enhanced.table;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.id.enhanced.SequenceStyleGenerator;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class BasicTableTest extends DatabaseSpecificFunctionalTestCase {
+ public BasicTableTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/table/Basic.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BasicTableTest.class );
+ }
+
+ public void testNormalBoundary() {
+ EntityPersister persister = sfi().getEntityPersister( Entity.class.getName() );
+ assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
+ SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
+
+ int count = 5;
+ Entity[] entities = new Entity[count];
+ Session s = openSession();
+ s.beginTransaction();
+ for ( int i = 0; i < count; i++ ) {
+ entities[i] = new Entity( "" + ( i + 1 ) );
+ s.save( entities[i] );
+ long expectedId = i + 1;
+ assertEquals( expectedId, entities[i].getId().longValue() );
+ assertEquals( expectedId, generator.getDatabaseStructure().getTimesAccessed() );
+ assertEquals( expectedId, generator.getOptimizer().getLastSourceValue() );
+ }
+ s.getTransaction().commit();
+
+ s.beginTransaction();
+ for ( int i = 0; i < count; i++ ) {
+ assertEquals( i + 1, entities[i].getId().intValue() );
+ s.delete( entities[i] );
+ }
+ s.getTransaction().commit();
+ s.close();
+
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Entity.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Entity.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Entity.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,34 @@
+package org.hibernate.test.idgen.enhanced.table;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Entity {
+ private Long id;
+ private String name;
+
+ public Entity() {
+ }
+
+ public Entity(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Demonstrates use of the enhanced table-based identifier
+ generator, using a hilo algorithm as the optimization (to
+ avoid hitting the database to generate each value).
+-->
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.table">
+
+ <class name="Entity" table="ID_SEQ_TBL_HILO_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
+ <param name="table_name">ID_SEQ_TBL_HILO_SEQ</param>
+ <param name="initial_value">1</param>
+ <param name="increment_size">10</param>
+ <param name="optimizer">hilo</param>
+ <param name="force_tbl_use">true</param>
+ </generator>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,69 @@
+package org.hibernate.test.idgen.enhanced.table;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.SequenceStyleGenerator;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class HiLoTableTest extends DatabaseSpecificFunctionalTestCase {
+ public HiLoTableTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/table/HiLo.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( HiLoTableTest.class );
+ }
+
+ public void testNormalBoundary() {
+ EntityPersister persister = sfi().getEntityPersister( Entity.class.getName() );
+ assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
+ SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
+ assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
+ OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer();
+
+ int increment = optimizer.getIncrementSize();
+ Entity[] entities = new Entity[ increment + 1 ];
+ Session s = openSession();
+ s.beginTransaction();
+ for ( int i = 0; i < increment; i++ ) {
+ entities[i] = new Entity( "" + ( i + 1 ) );
+ s.save( entities[i] );
+ long expectedId = i + 1;
+ assertEquals( expectedId, entities[i].getId().longValue() );
+ assertEquals( 1, generator.getOptimizer().getLastSourceValue() );
+ assertEquals( i + 1, optimizer.getLastValue() );
+ assertEquals( increment + 1, optimizer.getHiValue() );
+ }
+ // now force a "clock over"
+ entities[ increment ] = new Entity( "" + increment );
+ s.save( entities[ increment ] );
+ long expectedId = optimizer.getIncrementSize() + 1;
+ assertEquals( expectedId, entities[ optimizer.getIncrementSize() ].getId().longValue() );
+ assertEquals( 2, optimizer.getLastSourceValue() ); // initialization + clokc-over
+ assertEquals( increment + 1, optimizer.getLastValue() );
+ assertEquals( ( increment * 2 ) + 1, optimizer.getHiValue() );
+
+ s.getTransaction().commit();
+
+ s.beginTransaction();
+ for ( int i = 0; i < entities.length; i++ ) {
+ assertEquals( i + 1, entities[i].getId().intValue() );
+ s.delete( entities[i] );
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Demonstrates use of the enhanced table-based identifier
+ generator, using a hilo algorithm as the optimization (to
+ avoid hitting the database to generate each value).
+-->
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.table">
+
+ <class name="Entity" table="ID_SEQ_TBL_POOL_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
+ <param name="table_name">ID_SEQ_TBL_POOL_SEQ</param>
+ <param name="initial_value">1</param>
+ <param name="increment_size">10</param>
+ <param name="optimizer">pooled</param>
+ <param name="force_tbl_use">true</param>
+ </generator>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java (rev 0)
+++ trunk/Hibernate3/test/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java 2007-03-07 22:55:12 UTC (rev 11259)
@@ -0,0 +1,69 @@
+package org.hibernate.test.idgen.enhanced.table;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.SequenceStyleGenerator;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PooledTableTest extends DatabaseSpecificFunctionalTestCase {
+ public PooledTableTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/table/Pooled.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PooledTableTest.class );
+ }
+
+ public void testNormalBoundary() {
+ EntityPersister persister = sfi().getEntityPersister( Entity.class.getName() );
+ assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
+ SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
+ assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
+ OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer();
+
+ int increment = optimizer.getIncrementSize();
+ Entity[] entities = new Entity[ increment + 1 ];
+ Session s = openSession();
+ s.beginTransaction();
+ for ( int i = 0; i < increment; i++ ) {
+ entities[i] = new Entity( "" + ( i + 1 ) );
+ s.save( entities[i] );
+ long expectedId = i + 1;
+ assertEquals( expectedId, entities[i].getId().longValue() );
+ assertEquals( 2, generator.getDatabaseStructure().getTimesAccessed() ); // initialization calls table twice
+ assertEquals( increment + 1, optimizer.getLastSourceValue() ); // initialization calls table twice
+ assertEquals( i + 1, optimizer.getLastValue() );
+ assertEquals( increment + 1, optimizer.getLastSourceValue() );
+ }
+ // now force a "clock over"
+ entities[ increment ] = new Entity( "" + increment );
+ s.save( entities[ increment ] );
+ long expectedId = optimizer.getIncrementSize() + 1;
+ assertEquals( expectedId, entities[ optimizer.getIncrementSize() ].getId().longValue() );
+ assertEquals( 3, generator.getDatabaseStructure().getTimesAccessed() ); // initialization (2) + clock over
+ assertEquals( ( increment * 2 ) + 1, optimizer.getLastSourceValue() ); // initialization (2) + clock over
+ assertEquals( increment + 1, optimizer.getLastValue() );
+ s.getTransaction().commit();
+
+ s.beginTransaction();
+ for ( int i = 0; i < entities.length; i++ ) {
+ assertEquals( i + 1, entities[i].getId().intValue() );
+ s.delete( entities[i] );
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+}
17 years, 1 month
Hibernate SVN: r11258 - in branches/Branch_3_2/Hibernate3: test/org/hibernate/test/hql and 1 other directory.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2007-03-07 17:41:22 -0500 (Wed, 07 Mar 2007)
New Revision: 11258
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
Log:
HHH-2376 : error referencing the FQN of the class for special HQL .class property resolution
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java 2007-03-07 22:39:25 UTC (rev 11257)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java 2007-03-07 22:41:22 UTC (rev 11258)
@@ -93,11 +93,12 @@
}
public void lookupConstant(DotNode node) throws SemanticException {
- String text = getText( node );
+ String text = ASTUtil.getPathText( node );
Queryable persister = walker.getSessionFactoryHelper().findQueryableUsingImports( text );
if ( persister != null ) {
// the name of an entity class
final String discrim = persister.getDiscriminatorSQLValue();
+ node.setDataType( persister.getDiscriminatorType() );
if ( InFragment.NULL.equals(discrim) || InFragment.NOT_NULL.equals(discrim) ) {
throw new InvalidPathException( "subclass test not allowed for null or not null discriminator: '" + text + "'" );
}
@@ -180,10 +181,6 @@
node.setResolvedConstant( text );
}
- private String getText(AST node) {
- return ASTUtil.getPathText( node );
- }
-
public void processBoolean(AST constant) {
// TODO: something much better - look at the type of the other expression!
// TODO: Have comparisonExpression and/or arithmeticExpression rules complete the resolution of boolean nodes.
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 2007-03-07 22:39:25 UTC (rev 11257)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2007-03-07 22:41:22 UTC (rev 11258)
@@ -279,6 +279,14 @@
new SyntaxChecker( "from Animal an where an.class = Dog" ).checkAll();
}
+ public void testSpecialClassPropertyReferenceFQN() {
+ // tests relating to HHH-2376
+ new SyntaxChecker( "from Zoo zoo where zoo.class = org.hibernate.test.hql.PettingZoo" ).checkAll();
+ new SyntaxChecker( "select a.description from Animal a where a.class = org.hibernate.test.hql.Mammal" ).checkAll();
+ new SyntaxChecker( "from DomesticAnimal an where an.class = org.hibernate.test.hql.Dog" ).checkAll();
+ new SyntaxChecker( "from Animal an where an.class = org.hibernate.test.hql.Dog" ).checkAll();
+ }
+
public void testSubclassOrSuperclassPropertyReferenceInJoinedSubclass() {
// this is a long standing bug in Hibernate; see HHH-1631 for details and history
//
17 years, 1 month
Hibernate SVN: r11257 - in trunk/Hibernate3: test/org/hibernate/test/hql and 1 other directory.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2007-03-07 17:39:25 -0500 (Wed, 07 Mar 2007)
New Revision: 11257
Modified:
trunk/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java
trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
Log:
HHH-2376 : error referencing the FQN of the class for special HQL .class property resolution
Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java 2007-03-07 19:32:58 UTC (rev 11256)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/util/LiteralProcessor.java 2007-03-07 22:39:25 UTC (rev 11257)
@@ -93,11 +93,12 @@
}
public void lookupConstant(DotNode node) throws SemanticException {
- String text = getText( node );
+ String text = ASTUtil.getPathText( node );
Queryable persister = walker.getSessionFactoryHelper().findQueryableUsingImports( text );
if ( persister != null ) {
// the name of an entity class
final String discrim = persister.getDiscriminatorSQLValue();
+ node.setDataType( persister.getDiscriminatorType() );
if ( InFragment.NULL.equals(discrim) || InFragment.NOT_NULL.equals(discrim) ) {
throw new InvalidPathException( "subclass test not allowed for null or not null discriminator: '" + text + "'" );
}
@@ -180,10 +181,6 @@
node.setResolvedConstant( text );
}
- private String getText(AST node) {
- return ASTUtil.getPathText( node );
- }
-
public void processBoolean(AST constant) {
// TODO: something much better - look at the type of the other expression!
// TODO: Have comparisonExpression and/or arithmeticExpression rules complete the resolution of boolean nodes.
Modified: trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2007-03-07 19:32:58 UTC (rev 11256)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2007-03-07 22:39:25 UTC (rev 11257)
@@ -279,6 +279,14 @@
new SyntaxChecker( "from Animal an where an.class = Dog" ).checkAll();
}
+ public void testSpecialClassPropertyReferenceFQN() {
+ // tests relating to HHH-2376
+ new SyntaxChecker( "from Zoo zoo where zoo.class = org.hibernate.test.hql.PettingZoo" ).checkAll();
+ new SyntaxChecker( "select a.description from Animal a where a.class = org.hibernate.test.hql.Mammal" ).checkAll();
+ new SyntaxChecker( "from DomesticAnimal an where an.class = org.hibernate.test.hql.Dog" ).checkAll();
+ new SyntaxChecker( "from Animal an where an.class = org.hibernate.test.hql.Dog" ).checkAll();
+ }
+
public void testSubclassOrSuperclassPropertyReferenceInJoinedSubclass() {
// this is a long standing bug in Hibernate; see HHH-1631 for details and history
//
17 years, 1 month
Hibernate SVN: r11256 - in branches/Branch_3_2/Hibernate3: test/org/hibernate/test/collection/map and 1 other directory.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2007-03-07 14:32:58 -0500 (Wed, 07 Mar 2007)
New Revision: 11256
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/collection/PersistentMap.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/Parent.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java
Log:
HHH-2476 : PersistentMap#put
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/collection/PersistentMap.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/collection/PersistentMap.java 2007-03-07 19:30:27 UTC (rev 11255)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/collection/PersistentMap.java 2007-03-07 19:32:58 UTC (rev 11256)
@@ -153,21 +153,21 @@
public Object put(Object key, Object value) {
if ( isPutQueueEnabled() ) {
Object old = readElementByIndex( key );
- queueOperation( new Put( key, value, old ) );
- return old;
- }
- else {
- initialize( true );
- Object old = map.put( key, value );
- // would be better to use the element-type to determine
- // whether the old and the new are equal here; the problem being
- // we do not necessarily have access to the element type in all
- // cases
- if ( value != old ) {
- dirty();
+ if ( old != UNKNOWN ) {
+ queueOperation( new Put( key, value, old ) );
+ return old;
}
- return old;
}
+ initialize( true );
+ Object old = map.put( key, value );
+ // would be better to use the element-type to determine
+ // whether the old and the new are equal here; the problem being
+ // we do not necessarily have access to the element type in all
+ // cases
+ if ( value != old ) {
+ dirty();
+ }
+ return old;
}
/**
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/Parent.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/Parent.java 2007-03-07 19:30:27 UTC (rev 11255)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/Parent.java 2007-03-07 19:32:58 UTC (rev 11256)
@@ -34,4 +34,15 @@
public void setChildren(Map children) {
this.children = children;
}
+
+ public Child addChild(String name) {
+ Child child = new Child( name );
+ addChild( child );
+ return child;
+ }
+
+ public void addChild(Child child) {
+ child.setParent( this );
+ getChildren().put( child.getName(), child );
+ }
}
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java 2007-03-07 19:30:27 UTC (rev 11255)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java 2007-03-07 19:32:58 UTC (rev 11256)
@@ -72,4 +72,31 @@
session.getTransaction().commit();
session.close();
}
+
+ public void testPutAgainstUninitializedMap() {
+ // prepare map owner...
+ Session session = openSession();
+ session.beginTransaction();
+ Parent parent = new Parent( "p1" );
+ session.save( parent );
+ session.getTransaction().commit();
+ session.close();
+
+ // Now, reload the parent and test adding children
+ session = openSession();
+ session.beginTransaction();
+ parent = ( Parent ) session.get( Parent.class, parent.getName() );
+ parent.addChild( "c1" );
+ parent.addChild( "c2" );
+ session.getTransaction().commit();
+ session.close();
+
+ assertEquals( 2, parent.getChildren().size() );
+
+ session = openSession();
+ session.beginTransaction();
+ session.delete( parent );
+ session.getTransaction().commit();
+ session.close();
+ }
}
17 years, 1 month
Hibernate SVN: r11255 - in trunk/Hibernate3: test/org/hibernate/test/collection/map and 1 other directory.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2007-03-07 14:30:27 -0500 (Wed, 07 Mar 2007)
New Revision: 11255
Modified:
trunk/Hibernate3/src/org/hibernate/collection/PersistentMap.java
trunk/Hibernate3/test/org/hibernate/test/collection/map/Parent.java
trunk/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java
Log:
HHH-2476 : PersistentMap#put
Modified: trunk/Hibernate3/src/org/hibernate/collection/PersistentMap.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/collection/PersistentMap.java 2007-03-07 16:50:06 UTC (rev 11254)
+++ trunk/Hibernate3/src/org/hibernate/collection/PersistentMap.java 2007-03-07 19:30:27 UTC (rev 11255)
@@ -153,21 +153,21 @@
public Object put(Object key, Object value) {
if ( isPutQueueEnabled() ) {
Object old = readElementByIndex( key );
- queueOperation( new Put( key, value, old ) );
- return old;
- }
- else {
- initialize( true );
- Object old = map.put( key, value );
- // would be better to use the element-type to determine
- // whether the old and the new are equal here; the problem being
- // we do not necessarily have access to the element type in all
- // cases
- if ( value != old ) {
- dirty();
+ if ( old != UNKNOWN ) {
+ queueOperation( new Put( key, value, old ) );
+ return old;
}
- return old;
}
+ initialize( true );
+ Object old = map.put( key, value );
+ // would be better to use the element-type to determine
+ // whether the old and the new are equal here; the problem being
+ // we do not necessarily have access to the element type in all
+ // cases
+ if ( value != old ) {
+ dirty();
+ }
+ return old;
}
/**
Modified: trunk/Hibernate3/test/org/hibernate/test/collection/map/Parent.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/collection/map/Parent.java 2007-03-07 16:50:06 UTC (rev 11254)
+++ trunk/Hibernate3/test/org/hibernate/test/collection/map/Parent.java 2007-03-07 19:30:27 UTC (rev 11255)
@@ -34,4 +34,15 @@
public void setChildren(Map children) {
this.children = children;
}
+
+ public Child addChild(String name) {
+ Child child = new Child( name );
+ addChild( child );
+ return child;
+ }
+
+ public void addChild(Child child) {
+ child.setParent( this );
+ getChildren().put( child.getName(), child );
+ }
}
Modified: trunk/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java 2007-03-07 16:50:06 UTC (rev 11254)
+++ trunk/Hibernate3/test/org/hibernate/test/collection/map/PersistentMapTest.java 2007-03-07 19:30:27 UTC (rev 11255)
@@ -72,4 +72,31 @@
session.getTransaction().commit();
session.close();
}
+
+ public void testPutAgainstUninitializedMap() {
+ // prepare map owner...
+ Session session = openSession();
+ session.beginTransaction();
+ Parent parent = new Parent( "p1" );
+ session.save( parent );
+ session.getTransaction().commit();
+ session.close();
+
+ // Now, reload the parent and test adding children
+ session = openSession();
+ session.beginTransaction();
+ parent = ( Parent ) session.get( Parent.class, parent.getName() );
+ parent.addChild( "c1" );
+ parent.addChild( "c2" );
+ session.getTransaction().commit();
+ session.close();
+
+ assertEquals( 2, parent.getChildren().size() );
+
+ session = openSession();
+ session.beginTransaction();
+ session.delete( parent );
+ session.getTransaction().commit();
+ session.close();
+ }
}
17 years, 1 month
Hibernate SVN: r11254 - branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-03-07 11:50:06 -0500 (Wed, 07 Mar 2007)
New Revision: 11254
Modified:
branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
Log:
More user friendly warn when the transaction strategy has been overridden
Modified: branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
===================================================================
--- branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2007-03-06 05:23:23 UTC (rev 11253)
+++ branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2007-03-07 16:50:06 UTC (rev 11254)
@@ -45,6 +45,7 @@
import org.hibernate.QueryException;
import org.hibernate.TransientObjectException;
import org.hibernate.StaleObjectStateException;
+import org.hibernate.cfg.Environment;
import org.hibernate.ejb.transaction.JoinableCMTTransaction;
import org.hibernate.ejb.util.ConfigurationHelper;
import org.hibernate.engine.SessionFactoryImplementor;
@@ -551,7 +552,7 @@
);
}
else {
- log.warn( "Cannot join transaction, not a JoinableCMTTransaction" );
+ log.warn( "Cannot join transaction: do not override " + Environment.TRANSACTION_STRATEGY );
}
}
catch (HibernateException he) {
17 years, 1 month