Hibernate SVN: r11248 - branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-02-27 21:06:13 -0500 (Tue, 27 Feb 2007)
New Revision: 11248
Modified:
branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
Log:
typo
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-02-28 00:48:02 UTC (rev 11247)
+++ branches/Branch_3_2/HibernateExt/entitymanager/src/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2007-02-28 02:06:13 UTC (rev 11248)
@@ -313,7 +313,7 @@
public EntityTransaction getTransaction() {
if ( transactionType == PersistenceUnitTransactionType.JTA ) {
- throw new IllegalStateException( "JTA EntityManager cannot access a transactions" );
+ throw new IllegalStateException( "A JTA EntityManager cannot use getTransaction()" );
}
return tx;
}
17 years, 9 months
Hibernate SVN: r11247 - branches/Branch_3_2/HibernateExt/search/lib.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-02-27 19:48:02 -0500 (Tue, 27 Feb 2007)
New Revision: 11247
Modified:
branches/Branch_3_2/HibernateExt/search/lib/README.txt
Log:
typo
Modified: branches/Branch_3_2/HibernateExt/search/lib/README.txt
===================================================================
--- branches/Branch_3_2/HibernateExt/search/lib/README.txt 2007-02-27 02:17:25 UTC (rev 11246)
+++ branches/Branch_3_2/HibernateExt/search/lib/README.txt 2007-02-28 00:48:02 UTC (rev 11247)
@@ -1,5 +1,5 @@
-Hibernate Commons Annotations dependencies
-==========================================
+Hibernate Search dependencies
+=============================
Core
====
17 years, 9 months
Hibernate SVN: r11246 - in branches/Branch_3_2/HibernateExt/search: src and 9 other directories.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2007-02-26 21:17:25 -0500 (Mon, 26 Feb 2007)
New Revision: 11246
Added:
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java
branches/Branch_3_2/HibernateExt/search/src/test-resources/
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java
branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java
Removed:
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java
Modified:
branches/Branch_3_2/HibernateExt/search/build.xml
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java
branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java
Log:
Add JMS test
Add Switchable directory test
Modified: branches/Branch_3_2/HibernateExt/search/build.xml
===================================================================
--- branches/Branch_3_2/HibernateExt/search/build.xml 2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/build.xml 2007-02-27 02:17:25 UTC (rev 11246)
@@ -22,7 +22,7 @@
<property name="javac.source" value="1.5"/>
<property name="javac.target" value="1.5"/>
<property name="jdbc.dir" value="jdbc"/>
- <property name="common.dir" value="${basedir}/../common"/>
+ <property name="common.dir" value="${basedir}/../common"/>
<property name="jpa-api.jar" value="${basedir}/../jpa-api/build/ejb3-persistence.jar"/>
<property name="annotations.jar"
@@ -32,23 +32,54 @@
<import file="${common.dir}/common-build.xml"/>
+
+ <property name="build.testresources.dir" value="${build.dir}/testresources"/>
+ <property name="testresources.dir" value="${basedir}/src/test-resources"/>
- <path id="lib.moduleclass.path">
+ <!-- override order for JBossXB to bootstrap properly -->
+ <path id="junit.classpath">
+ <fileset dir="${lib.dir}">
+ <include name="*.jar"/>
+ </fileset>
+ <pathelement path="${classes.dir}"/>
+ <pathelement path="${testclasses.dir}"/>
+ <path refid="junit.moduleclasspath"/>
+ <path refid="lib.class.path"/>
+ <path location="${clover.jar}"/>
+ </path>
+ <!-- override order for JBossXB to bootstrap properly -->
+ <path id="lib.class.path">
+ <path location="${hibernate-core.jar}"></path>
+ <fileset dir="${hibernate-core.lib.dir}">
+ <include name="*.jar"/>
+ <exclude name="xml-apis.jar"/>
+ <exclude name="xerces*.jar"/>
+ </fileset>
+ <fileset dir="${lib.dir}">
+ <include name="*.jar"/>
+ </fileset>
+ <path refid="lib.moduleclass.path"/>
+ <pathelement path="${clover.jar}"/>
+ </path>
+
+
+ <path id="lib.moduleclass.path">
<pathelement location="${jpa-api.jar}"/>
<pathelement location="${commons-annotations.jar}"/>
</path>
<path id="junit.moduleclasspath">
- <pathelement location="${src.dir}"/>
+ <!-- order matters for JBoss XB proper bootstrap -->
+ <fileset dir="${lib.dir}/test">
+ <include name="*.jar"/>
+ <include name="*.zip"/>
+ </fileset>
+ <pathelement location="${src.dir}"/>
<pathelement location="${test.dir}"/>
<pathelement location="${annotations.jar}"/>
<fileset dir="${jdbc.dir}">
<include name="*.jar"/>
<include name="*.zip"/>
</fileset>
- <fileset dir="${lib.dir}/test">
- <include name="*.jar"/>
- <include name="*.zip"/>
- </fileset>
</path>
<target name="init">
@@ -58,6 +89,7 @@
<available file="${commons-annotations.jar}" type="file" property="commons-annotations.jar.available"/>
<available file="${annotations.jar}" type="file" property="annotations.jar.available"/>
<mkdir dir="${lib.dir}/test"/>
+ <mkdir dir="${build.testresources.dir}"/>
</target>
<target name="get.jpa-api" depends="init" unless="jpa-api.jar.available">
@@ -118,30 +150,48 @@
</javac>
</target>
+ <target name="prepare-test-resources" depends="compiletest">
+ <copy todir="${build.testresources.dir}">
+ <fileset dir="${testresources.dir}">
+ <include name="**/*.*"/>
+ </fileset>
+ </copy>
+ <mkdir dir="${build.testresources.dir}/jars"/>
+ <jar filesetmanifest="merge" jarfile="${build.testresources.dir}/jars/jms-slave.jar" >
+ <fileset dir="${testclasses.dir}">
+ <include name="org/hibernate/search/test/jms/slave/**.*"/>
+ </fileset>
+ </jar>
+ <jar filesetmanifest="merge" jarfile="${build.testresources.dir}/jars/jms-master.jar" >
+ <fileset dir="${testclasses.dir}">
+ <include name="org/hibernate/search/test/jms/master/**.*"/>
+ </fileset>
+ </jar>
+ </target>
- <!-- target name="junit" depends="compiletest">
- <mkdir dir="test_output"/>
- <junit fork="yes" printsummary="yes" haltonfailure="yes"
- forkmode="perBatch">
- <classpath>
- <fileset dir="${jdbc.dir}">
- <include name="**/*.jar"/>
- <include name="**/*.zip"/>
- </fileset>
- <path refid="lib.class.path"/>
- <pathelement path="${classes.dir}"/>
- <pathelement path="${src.dir}"/>
- <pathelement path="${test.dir}"/>
- </classpath>
- <formatter type="plain"/>
- <formatter type="xml"/>
- <batchtest fork="yes" todir="test_output" haltonfailure="no">
- <fileset dir="${classes.dir}">
- <include name="**/*Test.class"/>
- </fileset>
- </batchtest>
- </junit>
- </target -->
+ <target name="junit" depends="compiletest, prepare-test-resources">
+ <mkdir dir="test_output"/>
+ <junit forkmode="once" printsummary="yes" haltonfailure="yes">
+ <classpath>
+ <path path="${build.testresources.dir}"/>
+ <!-- dirset dir="${build.testresources.dir}">
+ <include name="**/*.jar"/>
+ <include name="**/*.par"/>
+ </dirset -->
+ <path refid="junit.classpath"/>
+
+ <fileset dir="${jdbc.dir}">
+ <include name="**/*.jar"/>
+ <include name="**/*.zip"/>
+ </fileset>
+ </classpath>
+ <formatter type="plain"/>
+ <formatter type="xml"/>
+ <batchtest fork="yes" todir="test_output" haltonfailure="no">
+ <fileset refid="junit.batchtestset"/>
+ </batchtest>
+ </junit>
+ </target>
<!-- Run a single unit test. -->
<target name="junitsingle" depends="compiletest"
Copied: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java (from rev 11235, branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java)
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,111 @@
+//$Id: $
+package org.hibernate.search.backend.impl.jms;
+
+import java.util.List;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.ObjectMessage;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Session;
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.util.ContextHelper;
+
+/**
+ * Implement the Hibernate Search controller responsible for processing the
+ * work send through JMS by the slave nodes.
+ *
+ * Note the subclass implementation has to implements javax.jms.MessageListener
+ * //TODO Ask Bill why it is required
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class AbstractJMSHibernateSearchController implements MessageListener {
+ private static Log log = LogFactory.getLog( AbstractJMSHibernateSearchController.class );
+
+ /**
+ * return the current or give a new session
+ * This session is not used per se, but is the link to access the Search configuration
+ *
+ * A typical EJB 3.0 usecase would be to get the session from the container (injected)
+ * eg in JBoss EJB 3.0
+ * <code>
+ * @PersistenceContext private Session session;
+ *
+ * protected Session getSession() {
+ * return session
+ * }
+ *
+ * eg in any container
+ * <code>
+ * @PersistenceContext private EntityManager entityManager;
+ *
+ * protected Session getSession() {
+ * return (Session) entityManager.getdelegate();
+ * }
+ */
+ protected abstract Session getSession();
+
+ /**
+ * Ensure to clean the resources after use.
+ * If the session has been directly or indirectly injected, this method is empty
+ */
+ protected abstract void cleanSessionIfNeeded(Session session);
+
+ /**
+ * Process the Hibernate Search work queues received
+ */
+ public void onMessage(Message message) {
+ if ( !( message instanceof ObjectMessage ) ) {
+ log.error( "Incorrect message type: " + message.getClass() );
+ return;
+ }
+ ObjectMessage objectMessage = (ObjectMessage) message;
+ List<Work> queue;
+ try {
+ queue = (List<Work>) objectMessage.getObject();
+ }
+ catch (JMSException e) {
+ log.error( "Unable to retrieve object from message: " + message.getClass(), e );
+ return;
+ }
+ catch (ClassCastException e) {
+ log.error( "Illegal object retrieved from message", e );
+ return;
+ }
+ Runnable worker = getWorker( queue );
+ worker.run();
+ }
+
+ private Runnable getWorker(List<Work> queue) {
+ //FIXME casting sucks becasue we do not control what get session from
+ Session session = getSession();
+ Runnable processor = null;
+
+ try {
+ SearchFactory factory = ContextHelper.getSearchFactory( session );
+ processor = factory.getBackendQueueProcessorFactory().getProcessor( queue );
+ }
+ finally {
+ cleanSessionIfNeeded(session);
+ }
+ return processor;
+ }
+
+ @PostConstruct
+ public void initialize() {
+ //init the source copy process
+ //TODO actually this is probably wrong since this is now part of the DP
+ }
+
+ @PreDestroy
+ public void shutdown() {
+ //stop the source copy process
+ //TODO actually this is probably wrong since this is now part of the DP
+ }
+}
Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java 2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -11,10 +11,9 @@
import javax.naming.InitialContext;
import javax.naming.NamingException;
-import org.hibernate.HibernateException;
import org.hibernate.search.Environment;
+import org.hibernate.search.SearchException;
import org.hibernate.search.SearchFactory;
-import org.hibernate.search.SearchException;
import org.hibernate.search.backend.BackendQueueProcessorFactory;
import org.hibernate.search.backend.Work;
@@ -40,7 +39,7 @@
}
public Runnable getProcessor(List<Work> queue) {
- return new JMSBackendQueueProcessor(queue, this);
+ return new JMSBackendQueueProcessor( queue, this );
}
@@ -58,22 +57,30 @@
}
public void prepareJMSTools() {
- if (jmsQueue != null && factory != null) return;
+ if ( jmsQueue != null && factory != null ) return;
try {
- InitialContext initialContext = getInitialContext(properties);
+ InitialContext initialContext = getInitialContext( properties );
factory = (QueueConnectionFactory) initialContext.lookup( jmsConnectionFactoryName );
jmsQueue = (Queue) initialContext.lookup( jmsQueueName );
}
catch (NamingException e) {
- throw new SearchException("Unable to lookup Search queue and connection factory", e);
+ throw new SearchException( "Unable to lookup Search queue ("
+ + ( jmsQueueName != null ?
+ jmsQueueName :
+ "null" ) + ") and connection factory ("
+ + ( jmsConnectionFactoryName != null ?
+ jmsConnectionFactoryName :
+ "null" ) + ")",
+ e
+ );
}
}
private InitialContext getInitialContext(Properties properties) throws NamingException {
Properties jndiProps = getJndiProperties( properties );
- if (jndiProps.size() == 0) {
- return new InitialContext( );
+ if ( jndiProps.size() == 0 ) {
+ return new InitialContext();
}
else {
return new InitialContext( jndiProps );
@@ -90,21 +97,21 @@
Properties result = new Properties();
while ( iter.hasNext() ) {
String prop = (String) iter.next();
- if ( prop.indexOf(JNDI_PREFIX) > -1 && !specialProps.contains(prop) ) {
+ if ( prop.indexOf( JNDI_PREFIX ) > -1 && !specialProps.contains( prop ) ) {
result.setProperty(
- prop.substring( JNDI_PREFIX.length()+1 ),
- properties.getProperty(prop)
- );
+ prop.substring( JNDI_PREFIX.length() + 1 ),
+ properties.getProperty( prop )
+ );
}
}
- String jndiClass = properties.getProperty(JNDI_PREFIX + "class");
- String jndiURL = properties.getProperty(JNDI_PREFIX + "url");
+ String jndiClass = properties.getProperty( JNDI_PREFIX + "class" );
+ String jndiURL = properties.getProperty( JNDI_PREFIX + "url" );
// we want to be able to just use the defaults,
// if JNDI environment properties are not supplied
// so don't put null in anywhere
- if (jndiClass != null) result.put( Context.INITIAL_CONTEXT_FACTORY, jndiClass);
- if (jndiURL != null) result.put(Context.PROVIDER_URL, jndiURL);
+ if ( jndiClass != null ) result.put( Context.INITIAL_CONTEXT_FACTORY, jndiClass );
+ if ( jndiURL != null ) result.put( Context.PROVIDER_URL, jndiURL );
return result;
}
Deleted: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java 2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -1,67 +0,0 @@
-//$Id: $
-package org.hibernate.search.backend.impl.jms;
-
-import java.util.List;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageListener;
-import javax.jms.ObjectMessage;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.hibernate.Session;
-import org.hibernate.engine.SessionImplementor;
-import org.hibernate.search.SearchFactory;
-import org.hibernate.search.backend.Work;
-import org.hibernate.search.util.ContextHelper;
-
-/**
- * @author Emmanuel Bernard
- */
-public abstract class JMSHibernateSearchController implements MessageListener {
- private static Log log = LogFactory.getLog( JMSHibernateSearchController.class );
-
- protected abstract Session getSession();
-
- public void onMessage(Message message) {
- if ( !( message instanceof ObjectMessage ) ) {
- log.error( "Incorrect message type: " + message.getClass() );
- return;
- }
- ObjectMessage objectMessage = (ObjectMessage) message;
- List<Work> queue;
- try {
- queue = (List<Work>) objectMessage.getObject();
- }
- catch (JMSException e) {
- log.error( "Unable to retrieve object from message: " + message.getClass(), e );
- return;
- }
- catch (ClassCastException e) {
- log.error( "Illegal object retrieved from message", e );
- return;
- }
- Runnable worker = getWorker( queue );
- worker.run();
- }
-
- private Runnable getWorker(List<Work> queue) {
- //FIXME casting sucks becasue we do not control what get session from
- SearchFactory factory = ContextHelper.getSearchFactoryBySFI( (SessionImplementor) getSession() );
- return factory.getBackendQueueProcessorFactory().getProcessor( queue );
- }
-
- @PostConstruct
- public void initialize() {
- //init the source copy process
- //TODO actually this is probably wrong since this is now part of the DP
- }
-
- @PreDestroy
- public void shutdown() {
- //stop the source copy process
- //TODO actually this is probably wrong since this is now part of the DP
- }
-}
Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -55,7 +55,7 @@
* classes must be immutable
*/
public FullTextQueryImpl(org.apache.lucene.search.Query query, Class[] classes, SessionImplementor session,
- ParameterMetadata parameterMetadata) {
+ ParameterMetadata parameterMetadata) {
//TODO handle flushMode
super( query.toString(), null, session, parameterMetadata );
this.luceneQuery = query;
@@ -75,6 +75,9 @@
SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );
//find the directories
Searcher searcher = buildSearcher( searchFactory );
+ if ( searcher == null ) {
+ return new IteratorImpl( new ArrayList<EntityInfo>(0), (Session) this.session);
+ }
try {
org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
Hits hits = searcher.search( query );
@@ -95,20 +98,24 @@
throw new HibernateException( "Unable to query Lucene index", e );
}
finally {
- if ( searcher != null ) try {
- searcher.close();
+ if ( searcher != null ) {
+ try {
+ searcher.close();
+ }
+ catch (IOException e) {
+ log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
+ }
}
- catch (IOException e) {
- log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
- }
}
}
- public ScrollableResults scroll() throws HibernateException {
+ public ScrollableResults scroll() throws HibernateException {
//keep the searcher open until the resultset is closed
- SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );;
+ SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );
+ ;
//find the directories
Searcher searcher = buildSearcher( searchFactory );
+ //FIXME: handle null searcher
Hits hits;
try {
org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
@@ -135,9 +142,10 @@
}
public List list() throws HibernateException {
- SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );;
+ SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );
//find the directories
Searcher searcher = buildSearcher( searchFactory );
+ if (searcher == null) return new ArrayList(0);
Hits hits;
try {
org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
@@ -156,21 +164,21 @@
//we don't face proxy casting issues since the exact class is extracted from the index
}
//then initialize the objects
- List excludedObects = new ArrayList();
- for ( Object element : result ) {
- try {
- Hibernate.initialize( element );
- }
- catch (ObjectNotFoundException e) {
- log.debug( "Object found in Search index but not in database: "
- + e.getEntityName() + " wih id " + e.getIdentifier() );
- excludedObects.add( element );
- }
- }
- if (excludedObects.size() > 0) {
- result.removeAll( excludedObects );
- }
- return result;
+ List excludedObects = new ArrayList();
+ for ( Object element : result ) {
+ try {
+ Hibernate.initialize( element );
+ }
+ catch (ObjectNotFoundException e) {
+ log.debug( "Object found in Search index but not in database: "
+ + e.getEntityName() + " wih id " + e.getIdentifier() );
+ excludedObects.add( element );
+ }
+ }
+ if ( excludedObects.size() > 0 ) {
+ result.removeAll( excludedObects );
+ }
+ return result;
}
catch (IOException e) {
throw new HibernateException( "Unable to query Lucene index", e );
@@ -264,7 +272,7 @@
throw new HibernateException( "Unable to read Lucene directory", e );
}
}
- else {
+ else if ( dirNbr == 1 ) {
try {
searcher = new IndexSearcher( directories.iterator().next() );
}
@@ -272,6 +280,9 @@
throw new HibernateException( "Unable to read Lucene directory", e );
}
}
+ else {
+ return null; //no indexed entity set up
+ }
return searcher;
}
Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java 2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -24,9 +24,12 @@
* Use a Lucene FSDirectory
* The base directory is represented by hibernate.search.<index>.indexBase
* The index is created in <base directory>/<index name>
+ * The copy directory is built from <sourceBase>/<index name>
+ * TODO explose source
*
* @author Emmanuel Bernard
*/
+//TODO rename copy?
public class FSMasterDirectoryProvider implements DirectoryProvider<FSDirectory> {
private static Log log = LogFactory.getLog( FSMasterDirectoryProvider.class );
private FSDirectory directory;
@@ -37,13 +40,16 @@
public void initialize(String directoryProviderName, Properties properties, SearchFactory searchFactory) {
//source guessing
- String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", properties );
- if (source == null)
+ String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", directoryProviderName, properties );
+ if ( source == null)
throw new IllegalStateException("FSMasterDirectoryProvider requires a viable source directory");
+ log.debug( "Source directory: " + source );
File indexDir = DirectoryProviderHelper.determineIndexDir( directoryProviderName, properties );
+ log.debug( "Index directory: " + indexDir );
String refreshPeriod = properties.getProperty( "refresh", "60" );
long period = Long.parseLong( refreshPeriod );
-
+ period *= 100 * 60; //per minute
+ log.debug("Refresh period " + period / 1000 + " mins");
try {
boolean create = !indexDir.exists();
indexName = indexDir.getCanonicalPath();
@@ -58,10 +64,10 @@
}
//copy to source
- if ( new File(source, "current1").exists() ) {
+ if ( new File( source, "current1").exists() ) {
current = 2;
}
- else if ( new File(source, "current2").exists() ) {
+ else if ( new File( source, "current2").exists() ) {
current = 1;
}
else {
@@ -69,12 +75,12 @@
current = 1;
}
String currentString = Integer.valueOf( current ).toString();
- File subDir = new File(source, currentString );
+ File subDir = new File( source, currentString );
FileHelper.synchronize( indexDir, subDir, true );
- new File(source, "current1").delete();
- new File(source, "current2").delete();
+ new File( source, "current1").delete();
+ new File( source, "current2").delete();
//TODO small hole, no file can be found here
- new File(source, "current" + currentString).createNewFile();
+ new File( source, "current" + currentString).createNewFile();
log.debug( "Current directory: " + current);
}
catch (IOException e) {
@@ -144,6 +150,7 @@
public void run() {
//TODO get rid of current and use the marker file instead?
+ long start = System.currentTimeMillis();
inProgress = true;
if (directoryProviderLock == null) {
directoryProviderLock = searchFactory.getLockableDirectoryProviders().get( directoryProvider );
@@ -159,6 +166,7 @@
File destinationFile = new File(destination, Integer.valueOf(index).toString() );
//TODO make smart a parameter
try {
+ log.trace("Copying " + sourceFile + " into " + destinationFile);
FileHelper.synchronize( sourceFile, destinationFile, true);
current = index;
}
@@ -168,11 +176,11 @@
inProgress = false;
return;
}
- if ( ! new File(source, "current" + oldIndex).delete() ) {
+ if ( ! new File(destination, "current" + oldIndex).delete() ) {
log.warn( "Unable to remove previous marker file from source of " + indexName );
}
try {
- new File(source, "current" + index).createNewFile();
+ new File(destination, "current" + index).createNewFile();
}
catch( IOException e ) {
log.warn( "Unable to create current marker in source of " + indexName, e );
@@ -182,6 +190,7 @@
directoryProviderLock.unlock();
inProgress = false;
}
+ log.trace( "Copy for " + indexName + " took " + (System.currentTimeMillis() - start) + " ms");
}
}
Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java 2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -37,20 +37,26 @@
public void initialize(String directoryProviderName, Properties properties, SearchFactory searchFactory) {
//source guessing
- String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", properties );
+ String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", directoryProviderName, properties );
if (source == null)
throw new IllegalStateException("FSSwitchableDirectoryProvider requires a viable source directory");
if ( ! new File(source, "current1").exists() && ! new File(source, "current2").exists() ) {
throw new IllegalStateException("No current marker in source directory");
}
+ log.debug( "Source directory: " + source );
File indexDir = DirectoryProviderHelper.determineIndexDir( directoryProviderName, properties );
+ log.debug( "Index directory: " + indexDir.getPath() );
String refreshPeriod = properties.getProperty( "refresh", "60" );
long period = Long.parseLong( refreshPeriod );
-
+ period *= 100 * 60; //per minute
+ log.debug("Refresh period " + period / 60000 + " mins");
try {
boolean create = !indexDir.exists();
indexName = indexDir.getCanonicalPath();
- if (create) indexDir.mkdir();
+ if (create) {
+ indexDir.mkdir();
+ log.debug("Initializing index directory " + indexName);
+ }
File subDir = new File( indexName, "1" );
create = ! subDir.exists();
@@ -81,9 +87,18 @@
current = 1;
File sourceFile = new File(source);
File destinationFile = new File(indexName, Integer.valueOf(current).toString() );
- //TODO make smart a parameter
+ int sourceCurrent;
+ if ( new File(sourceFile, "current1").exists() ) {
+ sourceCurrent = 1;
+ }
+ else if ( new File(sourceFile, "current2").exists() ) {
+ sourceCurrent = 2;
+ }
+ else {
+ throw new AssertionFailure("No current file marker found in source directory: " + source);
+ }
try {
- FileHelper.synchronize( sourceFile, destinationFile, true);
+ FileHelper.synchronize( new File(sourceFile, String.valueOf(sourceCurrent) ), destinationFile, true);
}
catch (IOException e) {
throw new HibernateException("Umable to synchonize directory: " + indexName, e);
@@ -148,7 +163,7 @@
executor.execute( copyTask );
}
else {
- log.info( "Skipping directory synchronization, previous work still in progress: " + indexName);
+ log.trace( "Skipping directory synchronization, previous work still in progress: " + indexName);
}
}
}
@@ -164,6 +179,7 @@
}
public void run() {
+ long start = System.currentTimeMillis();
try {
inProgress = true;
int oldIndex = current;
@@ -184,6 +200,7 @@
File destinationFile = new File(destination, Integer.valueOf(index).toString() );
//TODO make smart a parameter
try {
+ log.trace("Copying " + sourceFile + " into " + destinationFile);
FileHelper.synchronize( sourceFile, destinationFile, true);
current = index;
}
@@ -206,6 +223,7 @@
finally {
inProgress = false;
}
+ log.trace( "Copy for " + indexName + " took " + (System.currentTimeMillis() - start) + " ms");
}
}
Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java 2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -8,6 +8,8 @@
import org.hibernate.HibernateException;
import org.hibernate.AssertionFailure;
+import org.hibernate.search.SearchException;
+import org.hibernate.annotations.common.util.StringHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -21,7 +23,8 @@
* and checking for the file availability
*
*/
- public static String getSourceDirectory(String rootPropertyName, String relativePropertyName, Properties properties) {
+ public static String getSourceDirectory(String rootPropertyName, String relativePropertyName,
+ String directoryProviderName, Properties properties) {
//TODO check that it's a directory
String root = properties.getProperty( rootPropertyName );
String relative = properties.getProperty( relativePropertyName );
@@ -31,8 +34,8 @@
+ " and " + relativePropertyName + " " + relative != null ? relative : "<null>"
);
}
- if (relative == null) throw new HibernateException("source property mandatory");
- if (root == null) {
+ if (relative == null) relative = directoryProviderName;
+ if ( StringHelper.isEmpty( root ) ) {
log.debug( "No root directory, go with relative " + relative );
File sourceFile = new File(relative);
if ( ! sourceFile.exists() ) {
@@ -41,8 +44,10 @@
//else keep source as it
}
else {
- File sourceFile = new File(root, relative);
- if ( sourceFile.exists() ) {
+ File rootDir = new File(root);
+ if ( rootDir.exists() ) {
+ File sourceFile = new File(root, relative);
+ if (! sourceFile.exists() ) sourceFile.mkdir();
log.debug( "Get directory from root + relative");
try {
relative = sourceFile.getCanonicalPath();
@@ -52,12 +57,7 @@
}
}
else {
- sourceFile = new File(relative);
- log.debug( "Get directory from relative only");
- if ( ! sourceFile.exists() ) {
- throw new HibernateException("Unable to read source directory: " + relative);
- }
- //else keep source as it
+ throw new SearchException(rootPropertyName + " does not exist");
}
}
return relative;
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,143 @@
+//$Id: $
+package org.hibernate.search.test.directoryProvider;
+
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.lucene.analysis.StopAnalyzer;
+import org.apache.lucene.queryParser.QueryParser;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.event.PostDeleteEventListener;
+import org.hibernate.event.PostInsertEventListener;
+import org.hibernate.event.PostUpdateEventListener;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.util.FileHelper;
+import org.hibernate.search.event.FullTextIndexEventListener;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class FSSwitchableAndMasterDPTest extends MultipleSFTestCase {
+
+ public void testProperCopy() throws Exception {
+ Session s1 = getSessionFactories()[0].openSession( );
+ SnowStorm sn = new SnowStorm();
+ sn.setDate( new Date() );
+ sn.setLocation( "Dallas, TX, USA");
+
+ FullTextSession fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+ QueryParser parser = new QueryParser("id", new StopAnalyzer() );
+ List result = fts2.createFullTextQuery( parser.parse( "location:texas" ) ).list();
+ assertEquals( "No copy yet, fresh index expected", 0, result.size() );
+
+ s1.persist( sn );
+ s1.flush(); //we don' commit so we need to flush manually
+
+ fts2.close();
+ s1.close();
+
+ Thread.sleep( 2 * 60 * 100 + 10); //wait a bit more than 2 refresh (one master / one slave)
+
+ //temp test original
+ fts2 = Search.createFullTextSession( getSessionFactories()[0].openSession( ) );
+ result = fts2.createFullTextQuery( parser.parse( "location:dallas" ) ).list();
+ assertEquals( "Original should get one", 1, result.size() );
+
+ fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+ result = fts2.createFullTextQuery( parser.parse( "location:dallas" ) ).list();
+ assertEquals("First copy did not work out", 1, result.size() );
+
+ s1 = getSessionFactories()[0].openSession( );
+ sn = new SnowStorm();
+ sn.setDate( new Date() );
+ sn.setLocation( "Chennai, India");
+
+ s1.persist( sn );
+ s1.flush(); //we don' commit so we need to flush manually
+
+ fts2.close();
+ s1.close();
+
+ Thread.sleep( 2 * 60 * 100 + 10); //wait a bit more than 2 refresh (one master / one slave)
+
+ fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+ result = fts2.createFullTextQuery( parser.parse( "location:chennai" ) ).list();
+ assertEquals("Second copy did not work out", 1, result.size() );
+
+ s1 = getSessionFactories()[0].openSession( );
+ sn = new SnowStorm();
+ sn.setDate( new Date() );
+ sn.setLocation( "Melbourne, Australia");
+
+ s1.persist( sn );
+ s1.flush(); //we don' commit so we need to flush manually
+
+ fts2.close();
+ s1.close();
+
+ Thread.sleep( 2 * 60 * 100 + 10); //wait a bit more than 2 refresh (one master / one slave)
+
+ fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+ result = fts2.createFullTextQuery( parser.parse( "location:melbourne" ) ).list();
+ assertEquals("Third copy did not work out", 1, result.size() );
+
+ fts2.close();
+ }
+
+
+ protected void setUp() throws Exception {
+ File base = new File(".");
+ File root = new File(base, "lucenedirs");
+ root.mkdir();
+
+ File master = new File(root, "master/main");
+ master.mkdirs();
+ master = new File(root, "master/copy");
+ master.mkdirs();
+
+ File slave = new File(root, "slave");
+ slave.mkdir();
+
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ File base = new File(".");
+ File root = new File(base, "lucenedirs");
+ FileHelper.delete( root );
+ }
+
+ protected int getSFNbrs() {
+ return 2;
+ }
+
+ protected Class[] getMappings() {
+ return new Class[] {
+ SnowStorm.class
+ };
+ }
+
+ protected void configure(Configuration[] cfg) {
+ //master
+ cfg[0].setProperty( "hibernate.search.default.sourceBase", "./lucenedirs/master/copy");
+ cfg[0].setProperty( "hibernate.search.default.indexBase", "./lucenedirs/master/main");
+ cfg[0].setProperty( "hibernate.search.default.refresh", "1"); //every minute
+ cfg[0].setProperty( "hibernate.search.default.directory_provider", "org.hibernate.search.store.FSMasterDirectoryProvider");
+ cfg[0].getEventListeners().setPostDeleteEventListeners( new PostDeleteEventListener[]{ new FullTextIndexEventListener() } );
+ cfg[0].getEventListeners().setPostUpdateEventListeners( new PostUpdateEventListener[]{ new FullTextIndexEventListener() } );
+ cfg[0].getEventListeners().setPostInsertEventListeners( new PostInsertEventListener[]{ new FullTextIndexEventListener() } );
+
+ //slave(s)
+ cfg[1].setProperty( "hibernate.search.default.sourceBase", "./lucenedirs/master/copy");
+ cfg[1].setProperty( "hibernate.search.default.indexBase", "./lucenedirs/slave");
+ cfg[1].setProperty( "hibernate.search.default.refresh", "1"); //every minute
+ cfg[1].setProperty( "hibernate.search.default.directory_provider", "org.hibernate.search.store.FSSwitchableDirectoryProvider");
+ cfg[1].getEventListeners().setPostDeleteEventListeners( new PostDeleteEventListener[]{ new FullTextIndexEventListener() } );
+ cfg[1].getEventListeners().setPostUpdateEventListeners( new PostUpdateEventListener[]{ new FullTextIndexEventListener() } );
+ cfg[1].getEventListeners().setPostInsertEventListeners( new PostInsertEventListener[]{ new FullTextIndexEventListener() } );
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,95 @@
+//$Id: $
+package org.hibernate.search.test.directoryProvider;
+
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.AnnotationConfiguration;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Build multiple session factories from the same set of classes
+ * The configuration can be altered overriding the #configure() method
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class MultipleSFTestCase extends TestCase {
+
+ private static SessionFactory[] sessionFactories;
+ private static AnnotationConfiguration[] cfgs;
+ private static Dialect dialect;
+ private static Class lastTestClass;
+
+ protected abstract int getSFNbrs();
+
+ protected void buildSessionFactories(Class[] classes, String[] packages, String[] xmlFiles) throws Exception {
+ if (sessionFactories == null) sessionFactories = new SessionFactory[ getSFNbrs() ];
+ if (cfgs == null) cfgs = new AnnotationConfiguration[ getSFNbrs() ];
+ for (SessionFactory sf : sessionFactories ) if ( sf != null ) sf.close();
+ for (int sfIndex = 0 ; sfIndex < getSFNbrs() ; sfIndex++ ) {
+ cfgs[sfIndex] = new AnnotationConfiguration();
+ }
+ configure( cfgs );
+ for (int sfIndex = 0 ; sfIndex < getSFNbrs() ; sfIndex++ ) {
+ try {
+ if ( recreateSchema() ) {
+ cfgs[sfIndex].setProperty( Environment.HBM2DDL_AUTO, "create-drop" );
+ }
+ for ( int i = 0; i < packages.length; i++ ) {
+ cfgs[sfIndex].addPackage( packages[i] );
+ }
+ for ( int i = 0; i < classes.length; i++ ) {
+ cfgs[sfIndex].addAnnotatedClass( classes[i] );
+ }
+ for ( int i = 0; i < xmlFiles.length; i++ ) {
+ InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( xmlFiles[i] );
+ cfgs[sfIndex].addInputStream( is );
+ }
+ setDialect( Dialect.getDialect() );
+ sessionFactories[sfIndex] = cfgs[sfIndex].buildSessionFactory( /*new TestInterceptor()*/ );
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+ }
+
+ protected void setUp() throws Exception {
+ if ( sessionFactories == null || sessionFactories[0] == null || lastTestClass != getClass() ) {
+ buildSessionFactories( getMappings(), getAnnotatedPackages(), getXmlFiles() );
+ lastTestClass = getClass();
+ }
+ }
+
+ protected abstract Class[] getMappings();
+
+ protected String[] getAnnotatedPackages() {
+ return new String[]{};
+ }
+
+ protected String[] getXmlFiles() {
+ return new String[]{};
+ }
+
+ private void setDialect(Dialect dialect) {
+ MultipleSFTestCase.dialect = dialect;
+ }
+
+ protected Dialect getDialect() {
+ return dialect;
+ }
+
+ protected abstract void configure(Configuration[] cfg) ;
+
+ protected boolean recreateSchema() {
+ return true;
+ }
+
+ public static SessionFactory[] getSessionFactories() {
+ return sessionFactories;
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,58 @@
+//$Id: $
+package org.hibernate.search.test.directoryProvider;
+
+import java.util.Date;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+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.DateBridge;
+import org.hibernate.search.annotations.Resolution;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Indexed
+@Entity
+public class SnowStorm {
+ @Id
+ @GeneratedValue
+ @DocumentId
+ private Long id;
+
+ @Field(index = Index.UN_TOKENIZED)
+ @DateBridge( resolution = Resolution.DAY )
+ private Date date;
+
+ @Field(index = Index.TOKENIZED)
+ private String location;
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,152 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.naming.InitialContext;
+
+import org.apache.lucene.analysis.StopAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Query;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.search.Environment;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.backend.AddWork;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
+import org.hibernate.search.test.SearchTestCase;
+import org.jboss.deployers.spi.DeploymentException;
+import org.jboss.embedded.Bootstrap;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class JMSMasterTest extends SearchTestCase {
+
+ private Bootstrap bootstrap;
+
+ public void testMessageSending() throws Exception {
+ MyHibernateUtil.sessionFactory = getSessions();
+
+ //create an object wo trigggering indexing
+ Session s = openSession( );
+ s.getTransaction().begin();
+ s.connection().createStatement().executeUpdate(
+ "insert into TShirt_Master(id, logo, size) values( '1', 'JBoss balls', 'large')"
+ );
+ TShirt ts = (TShirt) s.get(TShirt.class, 1);
+ s.getTransaction().commit();
+ s.close();
+ //create the work queue to send
+ Document doc = new Document();
+ Field field = new Field( DocumentBuilder.CLASS_FIELDNAME, ts.getClass().getName(), Field.Store.YES, Field.Index.UN_TOKENIZED );
+ doc.add( field );
+ field = new Field("id", "1", Field.Store.YES, Field.Index.UN_TOKENIZED );
+ doc.add( field );
+ field = new Field("logo", ts.getLogo(), Field.Store.NO, Field.Index.TOKENIZED );
+ doc.add( field );
+ Work work = new AddWork(ts.getId(), ts.getClass(), doc );
+ List<Work> queue = new ArrayList<Work>();
+ queue.add( work );
+
+ //send the queue
+ InitialContext context = new InitialContext();
+ QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup( "java:/ConnectionFactory" );
+ Queue jmsQueue = (Queue) context.lookup( "queue/searchtest" );
+ QueueConnection cnn;
+ QueueSender sender;
+ QueueSession session;
+ cnn = factory.createQueueConnection();
+ //TODO make transacted parameterized
+ session = cnn.createQueueSession( false, QueueSession.AUTO_ACKNOWLEDGE );
+
+ ObjectMessage message = session.createObjectMessage();
+ message.setObject( (Serializable) queue );
+
+ sender = session.createSender( jmsQueue );
+ sender.send( message );
+
+ session.close();
+ cnn.close();
+
+ //wait for the message to be processed
+ Thread.sleep( 1000 );
+
+ FullTextSession ftSess = Search.createFullTextSession( openSession( ) );
+ ftSess.getTransaction().begin();
+ QueryParser parser = new QueryParser( "id", new StopAnalyzer() );
+ Query luceneQuery = parser.parse( "logo:jboss" );
+ org.hibernate.Query query = ftSess.createFullTextQuery( luceneQuery );
+ List result = query.list();
+ assertEquals( 1, result.size() );
+ ftSess.delete( result.get( 0 ) );
+ ftSess.getTransaction().commit();
+ ftSess.close();
+ }
+
+ protected void setUp() throws Exception {
+ bootstrap = startupEmbeddedJBoss();
+ try {
+ super.setUp();
+ }
+ catch( RuntimeException e ) {
+ try {
+ shutdownEmbeddedJBoss(bootstrap);
+ }
+ catch( Exception ee ) {
+ //swallow
+ }
+ throw e;
+ }
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ shutdownEmbeddedJBoss(bootstrap);
+ }
+
+ public static Bootstrap startupEmbeddedJBoss() {
+ try {
+ long start = System.currentTimeMillis();
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.bootstrap();
+ bootstrap.deployResource( "jars/jms-master.jar" );
+ System.out.println("JBoss Embedded boot time: " + (System.currentTimeMillis() - start) + " ms");
+ return bootstrap;
+ }
+ catch (DeploymentException e) {
+ throw new RuntimeException( "Failed to bootstrap", e );
+ }
+ }
+
+ public static void shutdownEmbeddedJBoss(Bootstrap bootstrap) {
+ bootstrap.shutdown();
+ }
+
+
+ protected void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.WORKER_BACKEND, "lucene" );
+ cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_CONNECTION_FACTORY, "java:/ConnectionFactory" );
+ cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_QUEUE, "queue/searchtest" );
+
+ }
+
+ protected Class[] getMappings() {
+ return new Class[] {
+ TShirt.class
+ };
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,27 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import javax.ejb.MessageDriven;
+import javax.ejb.ActivationConfigProperty;
+import javax.jms.MessageListener;
+
+import org.hibernate.search.backend.impl.jms.AbstractJMSHibernateSearchController;
+import org.hibernate.Session;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@MessageDriven(activationConfig = {
+ @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
+ @ActivationConfigProperty(propertyName="destination", propertyValue="queue/searchtest"),
+ @ActivationConfigProperty(propertyName="DLQMaxResent", propertyValue="1")
+ } )
+public class MDBSearchController extends AbstractJMSHibernateSearchController implements MessageListener {
+ protected Session getSession() {
+ return MyHibernateUtil.sessionFactory.openSession( );
+ }
+
+ protected void cleanSessionIfNeeded(Session session) {
+ session.close();
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,12 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import org.hibernate.SessionFactory;
+
+/**
+ * Don't do it at home ;-)
+ * @author Emmanuel Bernard
+ */
+public class MyHibernateUtil {
+ public static SessionFactory sessionFactory;
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,52 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Table;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+@Indexed
+@Table(name="TShirt_Master")
+public class TShirt {
+ @Id
+ @DocumentId
+ private int id;
+ @Field(index= Index.TOKENIZED)
+ private String logo;
+ private String size;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getLogo() {
+ return logo;
+ }
+
+ public void setLogo(String logo) {
+ this.logo = logo;
+ }
+
+ public String getSize() {
+ return size;
+ }
+
+ public void setSize(String size) {
+ this.size = size;
+ }
+}
+
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,116 @@
+//$Id: $
+package org.hibernate.search.test.jms.slave;
+
+import org.jboss.embedded.Bootstrap;
+import org.jboss.deployers.spi.DeploymentException;
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.Environment;
+import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class JMSSlaveTest extends SearchTestCase {
+
+ private Bootstrap bootstrap;
+
+
+ protected void setUp() throws Exception {
+ bootstrap = startupEmbeddedJBoss();
+ try {
+ super.setUp();
+ }
+ catch( RuntimeException e ) {
+ try {
+ shutdownEmbeddedJBoss(bootstrap);
+ }
+ catch( Exception ee ) {
+ //swallow
+ }
+ throw e;
+ }
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ shutdownEmbeddedJBoss(bootstrap);
+ }
+
+ public void testMessageSend() throws Exception {
+ SearchQueueChecker.reset();
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ TShirt ts = new TShirt();
+ ts.setLogo( "Boston" );
+ ts.setSize( "XXL" );
+ TShirt ts2 = new TShirt();
+ ts2.setLogo( "Mapple leaves" );
+ ts2.setSize( "L" );
+ s.persist( ts );
+ s.persist( ts2 );
+ tx.commit();
+ //need to sleep for the message consumption
+ Thread.sleep(500);
+ assertEquals( 1, SearchQueueChecker.queues );
+ assertEquals( 2, SearchQueueChecker.works );
+
+ SearchQueueChecker.reset();
+ s = openSession();
+ tx = s.beginTransaction();
+ ts = (TShirt) s.get( TShirt.class, ts.getId() );
+ ts.setLogo( "Peter pan" );
+ tx.commit();
+ //need to sleep for the message consumption
+ Thread.sleep(500);
+ assertEquals( 1, SearchQueueChecker.queues );
+ assertEquals( 2, SearchQueueChecker.works ); //one update = 2 works
+
+ SearchQueueChecker.reset();
+ s = openSession();
+ tx = s.beginTransaction();
+ s.delete( s.get( TShirt.class, ts.getId() ) );
+ s.delete( s.get( TShirt.class, ts2.getId() ) );
+ tx.commit();
+ //Need to sleep for the message consumption
+ Thread.sleep(500);
+ assertEquals( 1, SearchQueueChecker.queues );
+ assertEquals( 2, SearchQueueChecker.works );
+ s.close();
+ }
+
+ public static Bootstrap startupEmbeddedJBoss() {
+ try {
+ long start = System.currentTimeMillis();
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.bootstrap();
+ bootstrap.deployResource( "jars/jms-slave.jar" );
+ System.out.println("JBoss Embedded boot time: " + (System.currentTimeMillis() - start) + " ms");
+ return bootstrap;
+ }
+ catch (DeploymentException e) {
+ throw new RuntimeException( "Failed to bootstrap", e );
+ }
+ }
+
+ public static void shutdownEmbeddedJBoss(Bootstrap bootstrap) {
+ bootstrap.shutdown();
+ }
+
+
+ protected void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.WORKER_BACKEND, "jms" );
+ cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_CONNECTION_FACTORY, "java:/ConnectionFactory" );
+ cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_QUEUE, "queue/searchtest" );
+
+ }
+
+ protected Class[] getMappings() {
+ return new Class[]{
+ TShirt.class
+ };
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,49 @@
+//$Id: $
+package org.hibernate.search.test.jms.slave;
+
+import java.util.List;
+import javax.jms.MessageListener;
+import javax.jms.Message;
+import javax.jms.ObjectMessage;
+import javax.jms.JMSException;
+import javax.ejb.MessageDriven;
+import javax.ejb.ActivationConfigProperty;
+
+import org.hibernate.search.backend.Work;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@MessageDriven(activationConfig = {
+ @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
+ @ActivationConfigProperty(propertyName="destination", propertyValue="queue/searchtest"),
+ @ActivationConfigProperty(propertyName="DLQMaxResent", propertyValue="1")
+ } )
+public class SearchQueueChecker implements MessageListener {
+ public static int queues;
+ public static int works;
+
+ public static void reset() {
+ queues = 0;
+ works = 0;
+ }
+
+ public void onMessage(Message message) {
+ if (! (message instanceof ObjectMessage ) ) {
+ return;
+ }
+ ObjectMessage objectMessage = (ObjectMessage) message;
+ List<Work> queue;
+ try {
+ queue = (List<Work>) objectMessage.getObject();
+ }
+ catch (JMSException e) {
+ return;
+ }
+ catch( ClassCastException e ) {
+ return;
+ }
+ queues++;
+ works+=queue.size();
+ }
+}
Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java 2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,50 @@
+//$Id: $
+package org.hibernate.search.test.jms.slave;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+@Indexed
+public class TShirt {
+ @Id
+ @GeneratedValue
+ @DocumentId
+ private int id;
+ @Field(index= Index.TOKENIZED)
+ private String logo;
+ private String size;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getLogo() {
+ return logo;
+ }
+
+ public void setLogo(String logo) {
+ this.logo = logo;
+ }
+
+ public String getSize() {
+ return size;
+ }
+
+ public void setSize(String size) {
+ this.size = size;
+ }
+}
17 years, 9 months
Hibernate SVN: r11245 - trunk/Hibernate3/doc/reference.
by hibernate-commits@lists.jboss.org
Author: jdkim528
Date: 2007-02-25 05:26:24 -0500 (Sun, 25 Feb 2007)
New Revision: 11245
Modified:
trunk/Hibernate3/doc/reference/build.xml
Log:
Korean translation was updated...
Modified: trunk/Hibernate3/doc/reference/build.xml
===================================================================
--- trunk/Hibernate3/doc/reference/build.xml 2007-02-25 10:24:19 UTC (rev 11244)
+++ trunk/Hibernate3/doc/reference/build.xml 2007-02-25 10:26:24 UTC (rev 11245)
@@ -42,10 +42,10 @@
<antcall target="lang.all"><param name="lang" value="en"/></antcall>
<antcall target="lang.all"><param name="lang" value="zh-cn"/></antcall>
<antcall target="lang.all"><param name="lang" value="ja"/></antcall>
+ <antcall target="lang.all"><param name="lang" value="ko"/></antcall>
<!-- TODO: These translations need updating...
<antcall target="lang.all"><param name="lang" value="es"/></antcall>
<antcall target="lang.all"><param name="lang" value="fr"/></antcall>
- <antcall target="lang.all"><param name="lang" value="ko"/></antcall>
-->
</target>
17 years, 9 months
Hibernate SVN: r11244 - trunk/Hibernate3/doc/reference/ko/modules.
by hibernate-commits@lists.jboss.org
Author: jdkim528
Date: 2007-02-25 05:24:19 -0500 (Sun, 25 Feb 2007)
New Revision: 11244
Modified:
trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml
Log:
..
Modified: trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml 2007-02-25 05:36:11 UTC (rev 11243)
+++ trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml 2007-02-25 10:24:19 UTC (rev 11244)
@@ -493,7 +493,7 @@
<para>
당신은 또한 컴포넌트들 또는 composite 사용자 타입들, 또는 명명된 컴포넌트 타입들의 속성들을 사용할 수도 있다. 상세한 것은
- <xref linkend="queryhql-coomponents"/>를 보라.
+ <xref linkend="queryhql-components"/>를 보라.
</para>
<para>
17 years, 9 months
Hibernate SVN: r11243 - trunk/Hibernate3/doc/reference/ko.
by hibernate-commits@lists.jboss.org
Author: jdkim528
Date: 2007-02-25 00:36:11 -0500 (Sun, 25 Feb 2007)
New Revision: 11243
Modified:
trunk/Hibernate3/doc/reference/ko/master.xml
Log:
added blanks
Modified: trunk/Hibernate3/doc/reference/ko/master.xml
===================================================================
--- trunk/Hibernate3/doc/reference/ko/master.xml 2007-02-25 05:35:21 UTC (rev 11242)
+++ trunk/Hibernate3/doc/reference/ko/master.xml 2007-02-25 05:36:11 UTC (rev 11243)
@@ -111,14 +111,16 @@
</para>
</listitem>
<listitem>
- <para>FAQ들은 Hibernate 웹 사이트 상에 답변되어 있다.</para>
+ <para>
+ FAQ들은 Hibernate 웹 사이트 상에 답변되어 있다.
+ </para>
</listitem>
<listitem>
- <para>
- 제 3의 데모들, 예제들, 그리고 튜토리얼들은 Hibernate 웹 사이트 상에 링크되어
+ <para>
+ 제 3의 데모들, 예제들, 그리고 튜토리얼들은 Hibernate 웹 사이트 상에 링크되어
있다.
- </para>
- </listitem>
+ </para>
+ </listitem>
<listitem>
<para>
Hibernate 웹사이트 상의 공동체 영역은 설계 패턴과 다양한 통합 솔루션들(Tomcat,
17 years, 9 months
Hibernate SVN: r11242 - trunk/Hibernate3/doc/reference/ko/modules.
by hibernate-commits@lists.jboss.org
Author: jdkim528
Date: 2007-02-25 00:35:21 -0500 (Sun, 25 Feb 2007)
New Revision: 11242
Modified:
trunk/Hibernate3/doc/reference/ko/modules/tutorial.xml
Log:
Added warnings about session-per-operation anti-pattern
Modified: trunk/Hibernate3/doc/reference/ko/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/doc/reference/ko/modules/tutorial.xml 2007-02-25 05:34:40 UTC (rev 11241)
+++ trunk/Hibernate3/doc/reference/ko/modules/tutorial.xml 2007-02-25 05:35:21 UTC (rev 11242)
@@ -562,7 +562,7 @@
</sect2>
- <sect2 id="tutorial-firstapp-workingpersistence" revision="4">
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="5">
<title>객체 로딩과 객체 저장</title>
<para>
@@ -622,21 +622,40 @@
</para>
<para>
+
<literal>sessionFactory.getCurrentSession()</literal>은 무엇을 행하는가? 먼저
당신은 당신이 (<literal>HibernateUtil</literal> 덕분에 쉽게) <literal>SessionFactory</literal>을
당신이 소유하고 있다면, 원하는 만큼 어디서든 여러번 그것을 호출할 수 있다.
<literal>getCurrentSession()</literal> 메소드는 항상 "현재의" 작업 단위를 반환한다. 우리가
<literal>hibernate.cfg.xml</literal> 내에서 이 매커니즘에 대한 구성 옵션을 "thread"로 전환시켰음을 기억하는가?
- 그러므로 현재 작업 단위의 영역은 우리의 어플리케이션을 실행시키는 현재의 Java 쓰레드이다. 하지만 이것은
- 전부가 진실은 아니다. 그것이 처음으로 필요로 되고, <literal>getCurrentSession()</literal>을 첫번째로
- 호출할 때 <literal>Session</literal>이 시작된다. 그때 현째의 쓰레드에 대해 Hibernate에 의해 바인드 된다.
- 트랜잭션이 종료될 때 커밋 되거나 롤백되고, Hibernate는 또한 쓰레드로부터 <literal>Session</literal>을
- 바인드 해제시키고 당신을 위해 그것을 닫는다. 만일 당신이 <literal>getCurrentSession()</literal>을 다시
- 호출한다면, 당신은 새로운 <literal>Session</literal>을 얻고 새로운 작업단위를 시작할 수 있다. 이
- <emphasis>thread-bound</emphasis> 프로그래밍 모형은 Hibernate를 사용하는 가장 대중적인 방법이다.
+ 그러므로 작업의 현재 단위는 우리의 어플리케이션을 실행시키는 현재 자바 쓰레드에 묶여 있다. 하지만
+ 이것은 전체 그림이 아니며, 당신은 또한 scope(영역), 작업 단위가 시작될 때와 작업 단위가 종료될 때를
+ 고려해야 한다.
</para>
<para>
+ <literal>Session</literal>은 그것이 처음으로 필요로 되고 <literal>getCurrentSession()</literal>에 대한
+ 첫 번째 호출이 이루어질 때 시작된다. 그때 그것은 Hibernate에 의해 현재의 쓰레드에 바인드 된다.
+ 커밋되든 롤백되든 간에 트랜잭션이 종료되고, Hibernate가 자동적으로 그 쓰레드로부터 <literal>Session</literal>을
+ 바인드 해제시키고 당신을 위해 세션을 닫는다. 만일 당신이 <literal>getCurrentSession()</literal>을 다시
+ 호출한다면, 당신은 새로운 <literal>Session</literal>을 얻고 새로운 작업단위를 시작할 수 있다.
+ 이 <emphasis>thread-bound</emphasis> 프로그래밍 모형은 Hibernate를 사용하는 가장 대중적인 방법이다.
+ 왜냐하면 그것은 당신의 코드를 유연하게 계층화시키는 것을 허용해주기 때문이다(트랜잭션 경계 분할 코드는
+ 데이트 접근 코드와 구별지워질 수 있는데, 우리는 이 튜토리얼의 뒷부분에서 이것을 다룰 것이다.).
+ </para>
+
+ <para>
+ 작업 영역의 단위와 관련하여, Hibernate <literal>Session</literal>은 하나 또는 여러 개의 데이터베이스
+ 오퍼레이션들을 실행시키는데 사용될 수 있는가? 위의 예제는 하나의 오퍼레이션에 한 개의 <literal>Session</literal>을
+ 사용하고 있다. 이것은 순진한 일치이며, 예제는 어떤 다른 접근법을 보여주기에는 충분히 복잡하지 않다.
+ Hibernate <literal>Session</literal>의 scope(영역)은 유연하지만 당신은 결코 <emphasis>모든</emphasis>
+ 데이터베이스 오퍼레이션 각각에 대해 새로운 Hibernate <literal>Session</literal>을 사용하도록
+ 당신의 어플리케이션을 설계할 수 없다. 따라서 심지어 당신이 다음의 (매우 사소한) 예제들에서 여러 번
+ 그것을 볼 수 있을지라도 <emphasis>session-per-operation</emphasis>을 하나의 안티-패턴으로 간주하라.
+ 실제 (웹) 어플리케이션은 이 튜토리얼의 뒷부분에 보여진다.
+ </para>
+
+ <para>
트랜잭션 핸들링과 경계구분에 대한 추가 정보는 <xref linkend="transactions"/>을 살펴보라.
우리는 또한 앞의 예제에서 임의의 오류 처리와 롤백을 생략했다.
</para>
@@ -1201,7 +1220,7 @@
그것은 새로운 이벤트들을 입력하기 위한 HTML form을 제공한다.
</para>
- <sect2 id="tutorial-webapp-servlet" revision="1">
+ <sect2 id="tutorial-webapp-servlet" revision="2">
<title>기본 서블릿 작성하기</title>
<para>
@@ -1257,6 +1276,12 @@
사용하지 않는다).
</para>
+ <para>
+ 모든 데이터베이스 오퍼레이션 각각에 대해 새로운 Hibernate <literal>Session</literal>을 사용하지 <emphasis>말라</emphasis>.
+ 전체 요청에 대해 영역지워진 한 개의 Hibernate <literal>Session</literal>을 사용하라. 그것이 자동적으로 현재의 자바 쓰레드에 바인드되도록
+ <literal>getCurrentSession()</literal>을 사용하라.
+ </para>
+
<para>
다음으로, 요청의 가능한 액션들이 처리되고 응답 HTML이 렌더링된다. 우리는 곧장 그부분으로 갈 것이다.
</para>
17 years, 9 months
Hibernate SVN: r11241 - trunk/Hibernate3/doc/reference/ko/modules.
by hibernate-commits@lists.jboss.org
Author: jdkim528
Date: 2007-02-25 00:34:40 -0500 (Sun, 25 Feb 2007)
New Revision: 11241
Modified:
trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml
Log:
HHH-1851 : relax special 'id' property handling
Modified: trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml 2007-02-25 05:33:51 UTC (rev 11240)
+++ trunk/Hibernate3/doc/reference/ko/modules/query_hql.xml 2007-02-25 05:34:40 UTC (rev 11241)
@@ -1,6 +1,6 @@
-<chapter id="queryhql">
+<chapter id="queryhql" revision="1">
<title>HQL: 하이버네이트 질의 언어(Hibernate Query Language)</title>
-
+
<para>
Hibernate는 (아주 의도적으로) SQL과 매우 흡사하게 보이는 극히 강력한 질의 언어를 구비하고 있다. 그러나 그 구문에 의해 우롱당하지 말라;
HQL은 상속, 다형성 그리고 연관과 같은 개념들을 이해하여서, 전체적으로 객체 지향적이다.
@@ -15,12 +15,12 @@
<literal>org.hibernate.eg.Foo</literal>과 같지 않고 <literal>foo.barSet</literal>은
<literal>foo.BARSET</literal>과 같지 않다.
</para>
-
+
<para>
이 매뉴얼은 소문자 HQL 키워드를 사용한다. 몇몇 사용자들은 보다 나은 가독성을 위해 대문자 키워드들을 가진 질의들을 찾지만, 우리는
자바 코드 속에 삽입될 때 이 컨벤션이 추하다는 점을 발견한다.
</para>
-
+
</sect1>
<sect1 id="queryhql-from">
@@ -29,16 +29,16 @@
<para>
가장 간단한 가능한 Hibernate 질의는 다음 형식이다:
</para>
-
+
<programlisting><![CDATA[from eg.Cat]]></programlisting>
-
+
<para>
이것은 <literal>eg.Cat</literal> 클래스의 모든 인스턴스들을 간단하게 반환한다. 우리는 대개 클래스 이름을 수식할 필요가 없다.
왜냐하면, <literal>auto-import</literal>가 디폴트이기 때문이다. 따라서 우리는 대개 항상 단지 다음과 같이 작성한다:
</para>
-
+
<programlisting><![CDATA[from Cat]]></programlisting>
-
+
<para>
대개 당신은 한 개의 <emphasis>alias</emphasis>를 할당할 필요가 있을 것이다. 왜냐하면 당신은 질의의 다른 부분들에서
<literal>Cat</literal>을 참조하고자 원할 것이기 때문이다.
@@ -50,21 +50,21 @@
이 질의는 alias <literal>cat</literal>을 <literal>Cat</literal> 인스턴스들에 할당하여서, 우리는 나중에 질의 속에서
그 alias를 사용할 수 있을 것이다. <literal>as</literal> 키워드는 옵션이다; 우리는 또한 다음과 같이 작성할 수 있다:
</para>
-
+
<programlisting><![CDATA[from Cat cat]]></programlisting>
-
+
<para>
여러 개의 클래스들은 cartesian product(카티젼 곱) 또는 "크로스" 조인으로 귀결되어 나타날 수도 있다.
</para>
-
+
<programlisting><![CDATA[from Formula, Parameter]]></programlisting>
<programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
-
+
<para>
로컬 변수들에 대한 Java 네이밍 표준들과 일치되게, 첫 소문자를 사용하여 질의 alias들을 명명하는 것은 좋은 습관으로 간주된다
(예를 들면 <literal>domesticCat</literal>).
</para>
-
+
</sect1>
<sect1 id="queryhql-joins" revision="2">
@@ -109,7 +109,7 @@
</para>
</listitem>
</itemizedlist>
-
+
<para>
<literal>inner join</literal>, <literal>left outer join</literal>, 그리고 <literal>right outer join</literal>
구조체들이 약칭될 수 있다.
@@ -118,7 +118,7 @@
<programlisting><![CDATA[from Cat as cat
join cat.mate as mate
left join cat.kittens as kitten]]></programlisting>
-
+
<para>
당신은 HQL <literal>with</literal> 키워드를 사용하여 특별한 조인 조건들을 제공할 수 있다.
</para>
@@ -132,22 +132,22 @@
것을 허용해준다. 이것은 콜렉션의 경우에 특히 유용하다. 그것은 연관관계들과 콜렉션들에 대한 매핑 파일의 outer join과 lazy 선언들을
효율적으로 오버라이드 시킨다. 추가 정보는 <xref linkend="performance-fetching"/>을 보라.
</para>
-
+
<programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens]]></programlisting>
-
+
<para>
fetch join은 대개 alias를 할당할 필요가 없다. 왜냐하면, 연관된 객체들이 <literal>where</literal> 절(또는 어떤 다른 절)
속에 사용되지 않을 것이기 때문이다. 또한 연관된 객체들은 질의 결과들 속에 직접 반환되지 않는다. 대신 그것들은 부모 객체를 통해
접근될 수 있다. 우리가 alias를 필요로 할 수 있는 유일한 이유는 더 많은 콜렉션들을 재귀적으로 조인 페칭시키는 경우이다:
</para>
-
+
<programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens]]></programlisting>
-
+
<para>
(비록 <literal>scroll()</literal>이 사용될 수 있을지라도) <literal>fetch</literal> 구조체는
<literal>iterate()</literal>를 사용하여 호출되는 질의들 내에 사용될 수 없음을 노트하라. 이들 오퍼레이션들이 결과 행들에
@@ -159,12 +159,12 @@
또한 때때로 bag 매핑들에 대해 예기치 않은 결과들을 가져다주기 때문에, 당신이 이 경우에 당신의 질의들을 처방하는 방법에 대해 주의하라.
마지막으로 <literal>full join fetch</literal>와 <literal>right join fetch</literal>는 의미가 없다.
</para>
-
+
<para>
만일 당신이 (바이트코드 방편으로) property-레벨 lazy 페칭을 사용할 경우, Hibernate로 하여금 <literal>fetch all properties</literal>를
사용하여 (첫 번째 질의에서) lazy 프로퍼티들을 즉시 페치하도록 강제시키는 것이 가능하다.
</para>
-
+
<programlisting><![CDATA[from Document fetch all properties order by name]]></programlisting>
<programlisting><![CDATA[from Document doc fetch all properties where lower(doc.name) like '%cats%']]></programlisting>
@@ -191,6 +191,39 @@
<programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
</sect1>
+ <sect1 id="queryhql-identifier-property">
+ <title>식별자 속성 참조하기</title>
+
+ <para>
+ 개략적으로 말하자면, 엔티티의 식별자 속성을 참조하는 2가지 방법들이 존재한다:
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <emphasis>엔티티가 id로 명명된 비식별자 속성을 정의하지 않는다고 가정하면</emphasis>
+ 특별한 속성(소문자) <literal>id</literal>가 엔티티의 식별자 속성을 참조하는데 사용될 수 있다.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 만일 그 엔티티가 명명된 식별자 속성을 정의하는 경우, 당신은 그 속성 이름을 사용할 수도 있다.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ composite 식별자 속성들에 대한 참조들은 동일한 네이밍 규칙들을 따른다. 만일 그 엔티티가 id로 명명된 비식별자를
+ 갖고 있을 경우, composite 속성은 오직 그것의 정의된 명명에 의해 참조될 수 있다; 그밖의 경우 특별한 <literal>id</literal>
+ 속성이 식별자 속성을 참조하는데 사용될 수 있다.
+ </para>
+
+ <para>
+ 노트: 이것은 버전 3.2.2에서 시작될 때 현격하게 변경되었다. 이전 버전들에서, <literal>id</literal>는 <emphasis>항상</emphasis>
+ 그것의 실제 이름에 관계없이 식별자 속성을 참조했다. 그 결정의 분기점은 <literal>id</literal>로 명명된 비식별자 속성들이
+ Hibernate 질의들 내에서 결코 참조될 수 없었다는 점이었다.
+ </para>
+ </sect1>
+
<sect1 id="queryhql-select">
<title>select 절</title>
@@ -228,29 +261,29 @@
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
-
+
<para>
또는 <literal>List</literal>로서,
</para>
-
+
<programlisting><![CDATA[select new list(mother, offspr, mate.name)
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
-
+
<para>
또는 실제 typesafe 자바 객체로서,
</para>
-
+
<programlisting><![CDATA[select new Family(mother, mate, offspr)
from DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr]]></programlisting>
-
+
<para>
반환할 수도 있다.
</para>
-
+
<para>
당신은 <literal>as</literal>를 사용하여 select되는 표현식들에 alias들을 할당할 수 있다:
</para>
@@ -261,14 +294,14 @@
<para>
다음은 <literal>select new map</literal>과 함께 사용될 때 가장 유용하다:
</para>
-
+
<programlisting><![CDATA[select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
from Cat cat]]></programlisting>
<para>
이 질의는 select된 값들에 대한 alias로부터 한 개의 <literal>Map</literal>을 반환한다.
</para>
-
+
</sect1>
<sect1 id="queryhql-aggregation">
@@ -312,18 +345,18 @@
</para>
</listitem>
</itemizedlist>
-
+
<para>
당신은 select 절 속에 산술 연산자들, 연결 연산자, 그리고 인지된 SQL 함수들을 사용할 수 있다:
</para>
-
+
<programlisting><![CDATA[select cat.weight + sum(kitten.weight)
from Cat cat
join cat.kittens kitten
group by cat.id, cat.weight]]></programlisting>
<programlisting><![CDATA[select firstName||' '||initial||' '||upper(lastName) from Person]]></programlisting>
-
+
<para>
<literal>distinct</literal> 키워드와 all <literal>all</literal>가 사용될 수 있고 SQL의 경우와 동일한 의미를 갖는다.
</para>
@@ -333,7 +366,7 @@
select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
</sect1>
-
+
<sect1 id="queryhql-polymorphism">
<title>Polymorphic(다형성) 질의들</title>
@@ -349,24 +382,24 @@
명명할 수 있다. 질의는 그 클래스를 확장하거나 그 인터페이스를 구현하는 모든 영속 클래스들의 인스턴스들을 반환할 것이다. 다음 질의는
모든 영속 객체들을 반환할 것이다:
</para>
-
+
<programlisting><![CDATA[from java.lang.Object o]]></programlisting>
-
+
<para>
인터페이스 <literal>Named</literal>는 여러 가지 영속 클래스들에 의해 구현될 수도 있다:
</para>
-
+
<programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
-
+
<para>
이들 마지막 두 개의 질의들은 하나 이상의 SQL <literal>SELECT</literal>를 필요로 할 것임을 노트하라. 이것은 <literal>order by</literal>
절이 정확하게 전체 결과 셋을 순서지우지 않음을 의미한다.(그것은 또한 당신이 <literal>Query.scroll()</literal>을 사용하여
이들 질의들을 호출할 수 없음을 의미한다).
</para>
-
+
</sect1>
- <sect1 id="queryhql-where">
+ <sect1 id="queryhql-where" revision="1">
<title>where 절</title>
<para>
@@ -375,11 +408,11 @@
</para>
<programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
-
+
<para>
만일 한 개의 alias가 존재할 경우, 하나의 수식어가 붙은 프로퍼티 이름을 사용하라:
</para>
-
+
<programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
<para>
@@ -420,8 +453,8 @@
where cat.mate = mate]]></programlisting>
<para>
- 특별한 프로퍼티(소문자) <literal>id</literal>는 객체의 유일 식별자를 참조하는데 사용될 수 있다.(당신은 또한 그것의 프로퍼티
- 이름을 사용할 수도 있다.)
+ 특별한 프로퍼티(소문자) <literal>id</literal>는 객체의 유일 식별자를 참조하는데 사용될 수 있다.
+ 추가 정보는 <xref linkend="queryhql-identifier-property"/>를 보라.
</para>
<programlisting><![CDATA[from Cat as cat where cat.id = 123
@@ -433,23 +466,23 @@
</para>
<para>
- composite identifier(합성 식별자)들의 프로퍼티들이 또한 사용될 수 있다. <literal>Person</literal>이
- <literal>country</literal>와 <literal>medicareNumber</literal>로 구성된 composite identifier를 갖는다고
- 가정하자.
+ composite(합성) 식별자들의 프로퍼티들이 또한 사용될 수 있다. <literal>Person</literal>이
+ <literal>country</literal>와 <literal>medicareNumber</literal>로 구성된 composite 식별자를 갖는다고
+ 가정하자. 식별자 속성들을 참조하는 것과 관련된 추가 정보는 다시 <xref linkend="queryhql-identifier-property"/>를 보라.
</para>
<programlisting><![CDATA[from bank.Person person
-where person.id.country = 'AU'
+where person.id.country = 'AU'
and person.id.medicareNumber = 123456]]></programlisting>
<programlisting><![CDATA[from bank.Account account
-where account.owner.id.country = 'AU'
+where account.owner.id.country = 'AU'
and account.owner.id.medicareNumber = 123456]]></programlisting>
<para>
다시 한번, 두 번째 질의는 테이블 join을 필요로 하지 않는다.
</para>
-
+
<para>
마찬가지로, 특별한 프로퍼티 <literal>class</literal>는 다형적인 영속성(polymorphic persistence)의 경우에 인스턴스의
판별자(discriminator) 값에 액세스한다. where 절 속에 삽입된 Java 클래스 이름은 그것의 판별자(discriminator) 값으로
@@ -457,30 +490,26 @@
</para>
<programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
-
+
<para>
- 당신은 또한 컴포넌트들 또는 composite 사용자 정의 타입들의 (그리고 컴포넌트들의 컴포넌트들, 기타의) 프로퍼티들을
- 지정할 수도 있다. (컴포넌트의 프로퍼티와은 반대로) 컴포넌트 타입의 프로퍼티로 끝나는 경로-표현식을 사용하려고 결코 시도하지 말라.
- 예를 들어, 만일 <literal>store.owner</literal>가 컴포넌트 <literal>address</literal>를 가진 엔티티일 경우
+ 당신은 또한 컴포넌트들 또는 composite 사용자 타입들, 또는 명명된 컴포넌트 타입들의 속성들을 사용할 수도 있다. 상세한 것은
+ <xref linkend="queryhql-coomponents"/>를 보라.
</para>
- <programlisting><![CDATA[store.owner.address.city // okay
-store.owner.address // error!]]></programlisting>
-
<para>
"임의의" 타입은 다음 방법으로 join을 표현하는 것을 우리에게 허용해주는, 특별한 프로퍼티들 <literal>id</literal>와
<literal>class</literal>를 갖는다(여기서 <literal>AuditLog.item</literal>은 <literal><any></literal>로
매핑된 프로퍼티이다).
</para>
-
+
<programlisting><![CDATA[from AuditLog log, Payment payment
where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
-
+
<para>
<literal>log.item.class</literal>와 <literal>payment.class</literal>는 위의 질의 내에서 완전히 다른 데이터베이스
컬럼들의 값들을 참조할 것임을 노트하라.
</para>
-
+
</sect1>
<sect1 id="queryhql-expressions">
@@ -659,13 +688,13 @@
최소 인덱스과 최대 인덱스를 참조할 수 있다. 유사하게 당신은 <literal>minelement</literal>와
<literal>maxelement</literal> 함수를 사용하여 기본 타입을 가진 콜렉션의 최소 요소 및 최대 요소를 참조할 수 있다.
</para>
-
+
<programlisting><![CDATA[from Calendar cal where maxelement(cal.holidays) > current_date]]></programlisting>
-
+
<programlisting><![CDATA[from Order order where maxindex(order.items) > 100]]></programlisting>
<programlisting><![CDATA[from Order order where minelement(order.items) > 10000]]></programlisting>
-
+
<para>
SQL 함수들 <literal>any, some, all, exists, in</literal>은 콜렉션의 요소 또는 인덱스 세트(<literal>elements</literal>
함수와 <literal>indices</literal> 함수), 또는 서브질의의 결과를 전달했을 때 지원된다(아래를 보라).
@@ -688,11 +717,11 @@
<literal>minindex</literal>, <literal>maxindex</literal>, <literal>minelement</literal>,
<literal>maxelement</literal>-는 Hibernate3에서 where 절 내에서만 사용될 것임을 노트하라.
</para>
-
+
<para>
인덱싱 된 콜렉션들의 요소들(배열들, 리스트들, map들)은 인덱스에 의해 참조될 수 있다(where 절 안에서만):
</para>
-
+
<programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
<programlisting><![CDATA[select person from Person person, Calendar calendar
@@ -708,10 +737,10 @@
<para>
<literal>[]</literal> 내부의 표현식은 산술 표현실일 수 있다.
</para>
-
+
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item]]></programlisting>
-
+
<para>
HQL은 또한 one-to-many 연관 또는 값들을 가진 콜렉션의 요소들에 대해 미리 만들어진 <literal>index()</literal> 함수를
제공한다.
@@ -778,7 +807,7 @@
</para>
</sect1>
- <sect1 id="queryhql-grouping">
+ <sect1 id="queryhql-grouping" revision="1">
<title>group by 절</title>
<para>
@@ -810,95 +839,72 @@
<programlisting><![CDATA[select cat
from Cat cat
join cat.kittens kitten
-group by cat
+group by cat.id, cat.name, cat.other, cat.properties
having avg(kitten.weight) > 100
order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
<para>
<literal>group by</literal> 절도 <literal>order by</literal> 절 어느 것도 산술 표현식들을 포함할 수 없다는 점을
- 노트하라.
+ 노트하라. 또한 Hibernate는 현재 그룹지워진 엔티티를 확장하지 않아서 만일 <literal>cat</literal>의
+ 모든 속성들이 집계되지 않을 경우에 당신이 <literal>group by cat</literal>을 쓸수 없음을 노트하라.
+ 당신은 명시적으로 모든 집계되지 않는 속성들을 리스트해야 한다.
</para>
</sect1>
-
- <sect1 id="queryhql-subqueries" revision="2">
+
+ <sect1 id="queryhql-subqueries" revision="3">
<title>서브질의들</title>
<para>
subselect들을 지원하는 데이터베이스들의 경우, Hibernate는 질의들 내에 서브질의들을 지원한다. 서브질의는 괄호로 묶여져야
- 한다(자주 SQL 집계함수 호출에 의해). 심지어 서로 상관된 서브질의들(외부 질의 내에서 alias를 참조하는 서브질의들)이 허용된다.
+ 한다(자주 SQL 집계함수 호출에 의해). 심지어 서로 상관된 서브질의들(외부 질의 내에서 alias를 참조하는 서브질의들)이 허용된다.
</para>
- <programlisting><![CDATA[from Cat as fatcat
-where fatcat.weight > (
- select avg(cat.weight) from DomesticCat cat
+ <programlisting><![CDATA[from Cat as fatcat
+where fatcat.weight > (
+ select avg(cat.weight) from DomesticCat cat
)]]></programlisting>
- <programlisting><![CDATA[from DomesticCat as cat
-where cat.name = some (
- select name.nickName from Name as name
+ <programlisting><![CDATA[from DomesticCat as cat
+where cat.name = some (
+ select name.nickName from Name as name
)]]></programlisting>
-
- <programlisting><![CDATA[from Cat as cat
-where not exists (
- from Cat as mate where mate.mate = cat
+
+ <programlisting><![CDATA[from Cat as cat
+where not exists (
+ from Cat as mate where mate.mate = cat
)]]></programlisting>
- <programlisting><![CDATA[from DomesticCat as cat
-where cat.name not in (
- select name.nickName from Name as name
+ <programlisting><![CDATA[from DomesticCat as cat
+where cat.name not in (
+ select name.nickName from Name as name
)]]></programlisting>
- <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
from Cat as cat]]></programlisting>
<para>
- select 리스트 내에 있는 하나 이상의 표현식을 가진 서브질의들의 경우에 당신은 tuple 생성자를 사용할 수 있다:
- </para>
-
- <para>
- select 목록 내에 하나 이상의 표현식을 가진 서브질의들의 경우, 당신은 튜플(tuple) 구조를 사용할 수 있다:
- </para>
-
- <programlisting><![CDATA[from Cat as cat
-where not ( cat.name, cat.color ) in (
- select cat.name, cat.color from DomesticCat cat
-)]]></programlisting>
-
- <para>
HQL 서브질의들이 select 절 또는 where 절 내에서만 일어날 수 있음을 노트하라.
</para>
- <para>
- (Oracle 또는 HSQL이 아닌) 몇몇 데이터베이스들 상에서, 당신은 다른 컨텍스트들 내에서, 예를 들면 component들이나 composite
- 사용자 타입들을 질의할 때 tuple 생성자들을 사용할 수 있음을 노트하라:
- </para>
- <programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
-
<para>
- 이것을 더 풀어쓰면 다음과 동일하다:
+ 서브질의들은 또한 <literal>row value constructor</literal> 구문을 활용할 수 있다. 상세한 것은
+ <xref linkend="queryhql-tuple"/>를 보라.
</para>
-
- <programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
- <para>
- 당신이 이런 종류의 것을 행하는 것을 원하지 않을 수 있는 두 가지 좋은 이유들이 존재한다: 첫 번째로 데이터베이스 플랫폼들 사이에 완전하게
- 이식성이 없다; 두 번째로 그 질의는 이제 매핑 문서 속에 있는 프로퍼티들의 순서에 의존한다.
- </para>
-
</sect1>
<sect1 id="queryhql-examples">
<title>HQL 예제들</title>
-
+
<para>
-
+
Hibernate 질의들은 매우 강력하고 복잡할 수 있다. 사실, 질의 언어의 힘은 Hibernate의 주요 판매 포인트들 중 하나이다. 다음은
내가 최근의 프로젝트에서 사용했던 질의들과 매우 유사한 몇몇 예제 질의들이다. 당신이 작성하게 될 대부분의 질의들은 이것들보다 훨씬
간단하다는 점을 노트하라!
</para>
-
+
<para>
다음 질의는 특정 고객에 대한 모든 지불되지 않은 주문들의 주문 id, 항목들의 개수, 그리고 주문의 전체 합계값 그리고 주어진
최소 전체 합계를 전체 합계값에 따라 결과들을 순서지워서 반환한다. 가격 결정에 있어, 그것은 현재의 카타록을 사용한다.
@@ -906,7 +912,7 @@
<literal>CATALOG</literal>, <literal>PRICE</literal> 테이블들에 대한 네 개의 inner 조인들과 한 개의(상관지워지지
않은) subselect를 갖고 있다.
</para>
-
+
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
@@ -925,11 +931,11 @@
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]></programlisting>
-
+
<para>
괴물 같은 것! 실제로 실 생활에서, 나는 서브질의들을 매우 좋아하지 않아서, 나의 질의는 실제로 다음과 같았다:
</para>
-
+
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
@@ -943,7 +949,7 @@
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]></programlisting>
-
+
<para>
다음 질의는 현재 사용자에 의해 가장 최근의 상태 변경이 행해졌던 <literal>AWAITING_APPROVAL</literal> 상태에 있는 모든
지불들을 제외한, 각각의 상태에 있는 지불들의 개수를 카운트 한다. 그것은 <literal>PAYMENT</literal>,
@@ -971,7 +977,7 @@
만일 내가 <literal>statusChanges</literal> 콜렉션을 set가 아닌 list로 매핑했다면, 그 질의는 작성하기가 훨씬 더
간단했을 것이다.
</para>
-
+
<programlisting><![CDATA[select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
@@ -1085,7 +1091,7 @@
<para>
콜렉션 요소들은 질의 필터를 사용하여 순서(ordering)지워지거나 그룹지워질 수 도 있다:
</para>
-
+
<programlisting><![CDATA[Collection orderedCollection = s.filter( collection, "order by this.amount" );
Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );]]></programlisting>
@@ -1097,5 +1103,76 @@
</sect1>
+ <sect1 id="queryhql-components">
+ <title>컴포넌트들</title>
+
+ <para>
+ 컴포넌트들은 단지 간단한 값 유형들이 HQL 질의들 내에 사용될 수 있는 모든 방법으로 사용될 수 있다. 그것들은
+ <literal>select</literal> 절 내에 나타날 수 있다.
+ </para>
+
+ <programlisting><![CDATA[select p.name from from Person p]]></programlisting>
+ <programlisting><![CDATA[select p.name.first from from Person p]]></programlisting>
+
+ <para>
+ 여기서 Person의 name 속성은 컴포넌트이다. 컴포넌트들은 또한 <literal>where</literal> 절 내에 사용될 수 있다:
+ </para>
+
+ <programlisting><![CDATA[from from Person p where p.name = :name]]></programlisting>
+ <programlisting><![CDATA[from from Person p where p.name.first = :firstName]]></programlisting>
+
+ <para>
+ 컴포넌트들은 또한 <literal>order by</literal> 절 내에 사용될 수 있다:
+ </para>
+
+ <programlisting><![CDATA[from from Person p order by p.name]]></programlisting>
+ <programlisting><![CDATA[from from Person p order by p.name.first]]></programlisting>
+
+ <para>
+ 컴포넌트들에 대한 또 다른 공통적인 사용은 <xref linkend="queryhql-tuple">row value constructors</xref>에 있다.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tuple">
+ <title>Row value constructor 구문</title>
+
+ <para>
+ HQL은 ANSI SQL <literal>row value constructor</literal> 구문(종종 <literal>tuple</literal> 구문이라 명명된다)의 사용을 지원한다.
+ 비록 데이터베이스가 그 개념을 지원하지 않을지라도 그러하다. 여기서 우리는 전형적으로 컴포넌트들과 연관되어 있는 다중-값 비교들을
+ 일반적으로 언급하고 있다. name 컴포넌트를 정의하는 Person 엔티티를 검토하자:
+ </para>
+
+ <programlisting><![CDATA[from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt']]></programlisting>
+
+ <para>
+ 비록 약간 장황스럽지만 그것은 유효한 구문이다. 이것을 약간 더 간결하게 만들고
+ <literal>row value constructor</literal> 구문을 사용하는 것이 좋다:
+ </para>
+
+ <programlisting><![CDATA[from Person p where p.name=('John', 'Jingleheimer-Schmidt')]]></programlisting>
+
+ <para>
+ <literal>select</literal>절 내에 이것을 지정하는 것이 또한 유용할 수 있다:
+ </para>
+
+ <programlisting><![CDATA[select p.name from from Person p]]></programlisting>
+
+ <para>
+ <literal>row value constructor</literal> 구문을 사용하는 또 다른 경우는 다중 값들에 대해 비교할 필요가 있는
+ 서브 질의들을 사용할 때이다:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ 만일 당신이 이 구문을 사용하고자 원할 경우에 고려해야 할 한 가지는 질의가 메타데이터 내에 있는
+ 컴포넌트 서브-속성들의 순서에 종속될 것이라는 점이다.
+ </para>
+
+ </sect1>
+
</chapter>
17 years, 9 months
Hibernate SVN: r11240 - trunk/Hibernate3/doc/reference/ko/modules.
by hibernate-commits@lists.jboss.org
Author: jdkim528
Date: 2007-02-25 00:33:51 -0500 (Sun, 25 Feb 2007)
New Revision: 11240
Modified:
trunk/Hibernate3/doc/reference/ko/modules/filters.xml
Log:
HHH-2127 : document default filter conditions
Modified: trunk/Hibernate3/doc/reference/ko/modules/filters.xml
===================================================================
--- trunk/Hibernate3/doc/reference/ko/modules/filters.xml 2007-02-25 05:25:05 UTC (rev 11239)
+++ trunk/Hibernate3/doc/reference/ko/modules/filters.xml 2007-02-25 05:33:51 UTC (rev 11240)
@@ -6,7 +6,7 @@
필터</emphasis>는 특정 Hibernate 세션에 대해 이용 가능하게 되거나 이용 불가능하게 될 수도 있는 전역, 명명된 파라미터화 된 필터이다.
</para>
- <sect1 id="objectstate-filters">
+ <sect1 id="objectstate-filters" revision="1">
<title>Hibernate 필터들</title>
<para>
@@ -119,5 +119,20 @@
</sect1>
+ <para>
+ 필터가 정의된 후에는 그것 자신의 조건에 대해 각각 여러 개의 엔티티들 그리고/또는 콜렉션들에 첨가될 수 있다.
+ 조건들이 매번 동일할 때 그것은 지루할 수 있다. 따라서 <literal><filter-def/></literal>은
+ attribute 든 CDATA 든 어느것이든 디폴트 조건을 정의하는 것을 허용해준다:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter" condition="abc > xyz">...</filter-def>
+<filter-def name="myOtherFilter">abc=xyz</filter-def>]]></programlisting>
+
+ <para>
+ 그때 이 디폴트 조건은 그 필터가 어떤 조건을 지정함이 없이 어떤 것에 첨가될때마다 사용될 수 있다.
+ 이것은 당신이 특정한 경우에 디폴트 조건을 오버라이드 시키는 필터의 첨가 부분으로서 특정 조건을
+ 부여할 수 있음을 의미함을 노트하라.
+ </para>
+
</chapter>
17 years, 9 months
Hibernate SVN: r11239 - trunk/Hibernate3/doc/reference/ko/modules.
by hibernate-commits@lists.jboss.org
Author: jdkim528
Date: 2007-02-25 00:25:05 -0500 (Sun, 25 Feb 2007)
New Revision: 11239
Modified:
trunk/Hibernate3/doc/reference/ko/modules/batch.xml
Log:
Documented insert batch and identity
Modified: trunk/Hibernate3/doc/reference/ko/modules/batch.xml
===================================================================
--- trunk/Hibernate3/doc/reference/ko/modules/batch.xml 2007-02-25 05:23:03 UTC (rev 11238)
+++ trunk/Hibernate3/doc/reference/ko/modules/batch.xml 2007-02-25 05:25:05 UTC (rev 11239)
@@ -26,6 +26,11 @@
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+ <para id="disablebatching" revision="1">
+ 만일 당신이 <literal>identiy</literal> 식별자 생성자를 사용할 경우
+ Hibernate가 JDBC 레벨에서 투명하게 insert 배치작업을 사용불가능하게 한다는 점을 노트하라.
+ </para>
+
<para>
당신은 또한 second-level 캐시를 가진 상호작용이 완전하게 불가능한 프로세스 내에서 이런 종류의 작업을 행하고 싶어할 수도 있다:
</para>
17 years, 9 months